1 /*
2  * Copyright (c) 2021 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/text_field/text_field_element.h"
17 
18 #include "core/components/text_field/text_field_component.h"
19 #include "core/components/text_field/text_field_controller.h"
20 
21 namespace OHOS::Ace {
22 namespace {
23 
24 constexpr int32_t KEYBOARD_HEIGHT = 200;
25 
26 } // namespace
27 
~TextFieldElement()28 TextFieldElement::~TextFieldElement()
29 {
30     auto context = context_.Upgrade();
31     if (context) {
32         context->UnregisterSurfaceChangedCallback(callbackId_);
33     }
34 }
35 
Update()36 void TextFieldElement::Update()
37 {
38     RenderElement::Update();
39 
40     auto labelTarget = AceType::DynamicCast<LabelTarget>(component_);
41     if (labelTarget) {
42         auto trigger = labelTarget->GetTrigger();
43         if (trigger) {
44             auto weak = AceType::WeakClaim(this);
45             trigger->clickHandler_ = [weak]() {
46                 auto textField = weak.Upgrade();
47                 if (textField) {
48                     LOGI("Element update, request keyboard");
49                     textField->RequestKeyboard(true);
50                 }
51             };
52         }
53     }
54 
55     auto textField = AceType::DynamicCast<TextFieldComponent>(component_);
56     if (!textField) {
57         return;
58     }
59 
60     if (textField->GetTextFieldController()) {
61         textField->GetTextFieldController()->SetHandler(AceType::WeakClaim(this));
62     }
63     enabled_ = textField->IsEnabled();
64 
65     // If auto focus, request keyboard immediately.
66     if (textField->GetAutoFocus() && isFirstLoad_) {
67         LOGI("Auto focus, request keyboard");
68         RequestKeyboard(true);
69         isFirstLoad_ = false;
70     }
71 
72     auto context = context_.Upgrade();
73     if (context && callbackId_ <= 0) {
74         callbackId_ = context->RegisterSurfaceChangedCallback(
75             [weak = WeakClaim(this)](
76                 int32_t width, int32_t height, int32_t oldWidth, int32_t oldHeight, WindowSizeChangeReason type) {
77                 auto textField = weak.Upgrade();
78                 if (textField) {
79                     textField->OnSurfaceChanged(width, height, oldWidth, oldHeight);
80                 }
81             });
82     }
83 }
84 
CreateRenderNode()85 RefPtr<RenderNode> TextFieldElement::CreateRenderNode()
86 {
87     RefPtr<RenderNode> node = RenderElement::CreateRenderNode();
88 
89     auto renderNode = AceType::DynamicCast<RenderTextField>(node);
90     if (renderNode) {
91         renderNode->RegisterTapCallback([wp = AceType::WeakClaim(this)](bool isRequestKeyboard) {
92             auto sp = wp.Upgrade();
93             if (sp) {
94                 LOGI("tap callback, request keyboard");
95                 return sp->RequestKeyboard(false, isRequestKeyboard);
96             }
97             return false;
98         });
99         renderNode->SetNextFocusEvent([wp = AceType::WeakClaim(this)]() {
100             auto sp = wp.Upgrade();
101             if (sp) {
102                 auto pipeline = sp->context_.Upgrade();
103                 if (!pipeline) {
104                     LOGW("pipeline is null.");
105                     return;
106                 }
107                 sp->isNextAction_ = true;
108 
109                 KeyEvent keyEvent(KeyCode::KEY_DPAD_DOWN, KeyAction::UP);
110                 if (!pipeline->OnKeyEvent(keyEvent)) {
111                     LOGI("Key pressed change next focus, close keyboard");
112                     sp->CloseKeyboard();
113                 } else {
114                     // below textfield will auto open keyboard
115                     KeyEvent keyEventEnter(KeyCode::KEY_ENTER, KeyAction::UP);
116                     pipeline->OnKeyEvent(keyEventEnter);
117                 }
118             }
119         });
120 
121         renderNode->SetOnOverlayFocusChange([wp = AceType::WeakClaim(this)](bool isFocus) {
122             auto sp = wp.Upgrade();
123             if (sp) {
124                 if (!isFocus && !sp->isRequestFocus_) {
125                     LOGI("Overlay focus change, close keyboard");
126                     sp->CloseKeyboard();
127                 }
128             }
129         });
130     }
131     return node;
132 }
133 
OnKeyEvent(const KeyEvent & keyEvent)134 bool TextFieldElement::OnKeyEvent(const KeyEvent& keyEvent)
135 {
136     if (!enabled_) {
137         return false;
138     }
139 
140     if (editingMode_ && FocusNode::OnKeyEvent(keyEvent)) {
141         return true;
142     }
143 
144     if (editingMode_) {
145         auto textField = DynamicCast<RenderTextField>(renderNode_);
146         if (textField && textField->OnKeyEvent(keyEvent)) {
147             return true;
148         }
149     }
150 
151     // always use DOWN actions to trigger events
152     if (keyEvent.action != KeyAction::DOWN) {
153         return false;
154     }
155 
156     switch (keyEvent.code) {
157         case KeyCode::KEY_BACK:
158         case KeyCode::KEY_ESCAPE: {
159             bool editingMode = editingMode_;
160             LOGI("Escape key pressed, close keyboard");
161             CloseKeyboard();
162             // If not editingMode, mark the keyevent unhandled to let navigator pop page..
163             return editingMode;
164         }
165         case KeyCode::KEY_ENTER:
166         case KeyCode::KEY_NUMPAD_ENTER:
167         case KeyCode::KEY_DPAD_CENTER:
168             LOGI("Pad center key pressed, request keyboard");
169             RequestKeyboard(true);
170             return true;
171         case KeyCode::KEY_DPAD_LEFT:
172         case KeyCode::KEY_DPAD_RIGHT:
173         case KeyCode::KEY_DPAD_UP:
174         case KeyCode::KEY_DPAD_DOWN: {
175             bool result = editingMode_ && !isNextAction_;
176             isNextAction_ = false;
177             return result;
178         }
179         default:
180             return false;
181     }
182 }
183 
OnFocus()184 void TextFieldElement::OnFocus()
185 {
186     if (!enabled_) {
187         return;
188     }
189     LOGI("Textfield on focus");
190     auto textField = DynamicCast<RenderTextField>(renderNode_);
191     if (textField) {
192         textField->StartTwinkling();
193         textField->OnEditChange(true);
194     }
195     LOGI("On focus, request keyboard");
196     RequestKeyboard(true, false);
197     FocusNode::OnFocus();
198     textField->SetCanPaintSelection(true);
199     auto context = context_.Upgrade();
200     if (context && context->GetIsTabKeyPressed() && renderNode_) {
201         renderNode_->ChangeStatus(RenderStatus::FOCUS);
202     }
203 }
204 
OnBlur()205 void TextFieldElement::OnBlur()
206 {
207     if (!enabled_) {
208         return;
209     }
210     auto textField = DynamicCast<RenderTextField>(renderNode_);
211     if (textField) {
212         textField->HandleOnBlur();
213     }
214     LOGI("On blur, close keyboard");
215     CloseKeyboard();
216     FocusNode::OnBlur();
217     auto context = context_.Upgrade();
218     if (context && context->GetIsTabKeyPressed() && renderNode_) {
219         renderNode_->ChangeStatus(RenderStatus::BLUR);
220     }
221 }
222 
OnSurfaceChanged(int32_t width,int32_t height,int32_t oldWidth,int32_t oldHeight)223 void TextFieldElement::OnSurfaceChanged(int32_t width, int32_t height, int32_t oldWidth, int32_t oldHeight)
224 {
225     // If height of surface append beyond 200, we think soft keyboard is closed.
226     if (oldWidth == width && height - oldHeight > KEYBOARD_HEIGHT) {
227         editingMode_ = false;
228     }
229 }
230 
CloseKeyboard(bool forceClose)231 void TextFieldElement::CloseKeyboard(bool forceClose)
232 {
233     isRequestFocus_ = false;
234     auto textField = DynamicCast<RenderTextField>(renderNode_);
235     if (textField) {
236         if (textField->CloseKeyboard(forceClose)) {
237             editingMode_ = false;
238         }
239     }
240 }
241 
RequestKeyboard(bool needStartTwinkling,bool needShowSoftKeyboard)242 bool TextFieldElement::RequestKeyboard(bool needStartTwinkling, bool needShowSoftKeyboard)
243 {
244     if (!enabled_) {
245         return false;
246     }
247     isRequestFocus_ = true;
248     if (RequestFocusImmediately()) {
249         auto textField = DynamicCast<RenderTextField>(renderNode_);
250         if (textField) {
251             if (textField->RequestKeyboard(!editingMode_, needStartTwinkling, needShowSoftKeyboard)) {
252                 editingMode_ = true;
253             }
254             textField->OnEditChange(true);
255         }
256         return true;
257     } else {
258         LOGW("Request keyboard failed because the textfield is unfocusable");
259         isRequestFocus_ = false;
260         return false;
261     }
262 }
263 
ShowError(const std::string & errorText)264 void TextFieldElement::ShowError(const std::string& errorText)
265 {
266     auto textField = DynamicCast<RenderTextField>(renderNode_);
267     if (textField) {
268         textField->ShowError(errorText);
269     }
270 }
271 
Delete()272 void TextFieldElement::Delete()
273 {
274     auto textField = DynamicCast<RenderTextField>(renderNode_);
275     if (!textField) {
276         return;
277     }
278     auto value = textField->GetEditingValue();
279     if (value.text.empty()) {
280         LOGI("Delete and text empty, request keyboard");
281         RequestKeyboard(true);
282         return;
283     }
284     if (editingMode_) {
285         auto start = value.selection.GetStart();
286         auto end = value.selection.GetEnd();
287         if (start >= 0 && end > 0) {
288             textField->Delete(start == end ? start - 1 : start, end);
289         }
290     } else {
291         textField->Delete(static_cast<int32_t>(value.GetWideText().size()) - 1,
292             static_cast<int32_t>(value.GetWideText().size()));
293     }
294     LOGI("Delete, request keyboard");
295     RequestKeyboard(true);
296 }
297 
Insert(const std::string & args)298 void TextFieldElement::Insert(const std::string& args)
299 {
300     auto textField = DynamicCast<RenderTextField>(renderNode_);
301     if (!textField) {
302         return;
303     }
304     textField->Insert(args);
305 }
306 
307 } // namespace OHOS::Ace
308