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