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_picker/textpicker_pattern.h"
17
18 #include <cstdint>
19 #include <securec.h>
20
21 #include "base/i18n/localization.h"
22 #include "base/geometry/dimension.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/utils/utils.h"
25 #include "core/components/picker/picker_theme.h"
26 #include "core/components_ng/pattern/button/button_pattern.h"
27 #include "core/components_ng/pattern/text/text_layout_property.h"
28 #include "core/components_ng/pattern/text/text_pattern.h"
29 #include "core/components_ng/pattern/text_picker/textpicker_column_pattern.h"
30 #include "core/components_ng/pattern/text_picker/textpicker_event_hub.h"
31 #include "core/components_ng/pattern/text_picker/textpicker_layout_property.h"
32 #include "core/components_ng/pattern/text_picker/toss_animation_controller.h"
33 #include "core/components_ng/property/calc_length.h"
34 #include "core/pipeline_ng/ui_task_scheduler.h"
35
36 namespace OHOS::Ace::NG {
37 namespace {
38 // TODO datepicker style modification
39 const Dimension PRESS_INTERVAL = 4.0_vp;
40 const Dimension PRESS_RADIUS = 8.0_vp;
41 constexpr uint32_t RATE = 2;
42 const Dimension OFFSET = 3.5_vp;
43 const Dimension OFFSET_LENGTH = 5.5_vp;
44 const Dimension DIALOG_OFFSET = 1.0_vp;
45 const Dimension DIALOG_OFFSET_LENGTH = 1.0_vp;
46 constexpr uint32_t HALF = 2;
47 const Dimension FOUCS_WIDTH = 2.0_vp;
48 const Dimension MARGIN_SIZE = 12.0_vp;
49 } // namespace
50
OnAttachToFrameNode()51 void TextPickerPattern::OnAttachToFrameNode()
52 {
53 auto host = GetHost();
54 CHECK_NULL_VOID(host);
55 host->GetRenderContext()->SetClipToFrame(true);
56 host->GetRenderContext()->UpdateClipEdge(true);
57 }
58
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)59 bool TextPickerPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
60 {
61 CHECK_NULL_RETURN_NOLOG(config.frameSizeChange, false);
62 CHECK_NULL_RETURN(dirty, false);
63 SetButtonIdeaSize();
64 return true;
65 }
66
OnLanguageConfigurationUpdate()67 void TextPickerPattern::OnLanguageConfigurationUpdate()
68 {
69 auto buttonConfirmNode = weakButtonConfirm_.Upgrade();
70 CHECK_NULL_VOID(buttonConfirmNode);
71 auto confirmNode = buttonConfirmNode->GetFirstChild();
72 auto confirmNodeLayout = AceType::DynamicCast<FrameNode>(confirmNode)->GetLayoutProperty<TextLayoutProperty>();
73 confirmNodeLayout->UpdateContent(Localization::GetInstance()->GetEntryLetters("common.ok"));
74
75 auto buttonCancelNode = weakButtonCancel_.Upgrade();
76 CHECK_NULL_VOID(buttonCancelNode);
77 auto cancelNode = buttonCancelNode->GetFirstChild();
78 auto cancelNodeLayout = AceType::DynamicCast<FrameNode>(cancelNode)->GetLayoutProperty<TextLayoutProperty>();
79 cancelNodeLayout->UpdateContent(Localization::GetInstance()->GetEntryLetters("common.cancel"));
80 }
81
SetButtonIdeaSize()82 void TextPickerPattern::SetButtonIdeaSize()
83 {
84 auto host = GetHost();
85 CHECK_NULL_VOID(host);
86 auto context = host->GetContext();
87 CHECK_NULL_VOID(context);
88 auto pickerTheme = context->GetTheme<PickerTheme>();
89 CHECK_NULL_VOID(pickerTheme);
90 auto children = host->GetChildren();
91 for (const auto& child : children) {
92 auto stackNode = DynamicCast<FrameNode>(child);
93 auto width = stackNode->GetGeometryNode()->GetFrameSize().Width();
94 auto buttonNode = DynamicCast<FrameNode>(child->GetFirstChild());
95 auto buttonLayoutProperty = buttonNode->GetLayoutProperty<ButtonLayoutProperty>();
96 buttonLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
97 buttonLayoutProperty->UpdateType(ButtonType::NORMAL);
98 buttonLayoutProperty->UpdateBorderRadius(BorderRadiusProperty(PRESS_RADIUS));
99 auto buttonHeight = CalculateHeight() - PRESS_INTERVAL.ConvertToPx() * RATE;
100 if (resizeFlag_) {
101 buttonHeight = resizePickerItemHeight_ - PRESS_INTERVAL.ConvertToPx() * RATE;
102 }
103 buttonLayoutProperty->
104 UpdateUserDefinedIdealSize(CalcSize(CalcLength(width - PRESS_INTERVAL.ConvertToPx() * RATE),
105 CalcLength(buttonHeight)));
106 auto buttonConfirmRenderContext = buttonNode->GetRenderContext();
107 buttonConfirmRenderContext->UpdateBackgroundColor(Color::TRANSPARENT);
108 buttonNode->MarkModifyDone();
109 }
110 }
111
OnModifyDone()112 void TextPickerPattern::OnModifyDone()
113 {
114 OnColumnsBuilding();
115 FlushOptions();
116 CalculateHeight();
117 if (cascadeOptions_.size() > 0) {
118 SetChangeCallback([weak = WeakClaim(this)](const RefPtr<FrameNode>& tag,
119 bool add, uint32_t index, bool notify) {
120 auto refPtr = weak.Upgrade();
121 CHECK_NULL_VOID_NOLOG(refPtr);
122 refPtr->HandleColumnChange(tag, add, index, notify);
123 });
124 }
125 SetEventCallback([weak = WeakClaim(this)](bool refresh) {
126 auto refPtr = weak.Upgrade();
127 CHECK_NULL_VOID_NOLOG(refPtr);
128 refPtr->FireChangeEvent(refresh);
129 });
130 auto host = GetHost();
131 CHECK_NULL_VOID(host);
132 auto focusHub = host->GetFocusHub();
133 CHECK_NULL_VOID_NOLOG(focusHub);
134 InitOnKeyEvent(focusHub);
135 InitDisabled();
136 }
137
SetEventCallback(EventCallback && value)138 void TextPickerPattern::SetEventCallback(EventCallback&& value)
139 {
140 auto host = GetHost();
141 CHECK_NULL_VOID(host);
142 auto children = host->GetChildren();
143 for (const auto& child : children) {
144 auto stackNode = DynamicCast<FrameNode>(child);
145 CHECK_NULL_VOID(stackNode);
146 auto childNode = DynamicCast<FrameNode>(stackNode->GetLastChild());
147 CHECK_NULL_VOID(childNode);
148 auto pickerColumnPattern = childNode->GetPattern<TextPickerColumnPattern>();
149 CHECK_NULL_VOID(pickerColumnPattern);
150 pickerColumnPattern->SetEventCallback(std::move(value));
151 }
152 }
153
FireChangeEvent(bool refresh)154 void TextPickerPattern::FireChangeEvent(bool refresh)
155 {
156 auto frameNodes = GetColumnNodes();
157 std::vector<std::string> value;
158 std::vector<double> index;
159 for (auto it : frameNodes) {
160 CHECK_NULL_VOID(it.second);
161 auto textPickerColumnPattern = it.second->GetPattern<TextPickerColumnPattern>();
162 if (refresh) {
163 auto currentIndex = textPickerColumnPattern->GetCurrentIndex();
164 index.emplace_back(currentIndex);
165 auto currentValue = textPickerColumnPattern->GetOption(currentIndex);
166 value.emplace_back(currentValue);
167 }
168 }
169 auto textPickerEventHub = GetEventHub<TextPickerEventHub>();
170 CHECK_NULL_VOID(textPickerEventHub);
171 textPickerEventHub->FireChangeEvent(value, index);
172 textPickerEventHub->FireDialogChangeEvent(GetSelectedObject(true, 1));
173 }
174
InitDisabled()175 void TextPickerPattern::InitDisabled()
176 {
177 auto host = GetHost();
178 CHECK_NULL_VOID(host);
179 auto eventHub = host->GetEventHub<EventHub>();
180 CHECK_NULL_VOID(eventHub);
181 auto renderContext = host->GetRenderContext();
182 enabled_ = eventHub->IsEnabled();
183 }
184
GetColumnNode()185 RefPtr<FrameNode> TextPickerPattern::GetColumnNode()
186 {
187 auto host = GetHost();
188 CHECK_NULL_RETURN(host, nullptr);
189
190 auto stackNode = host->GetChildAtIndex(focusKeyID_);
191 CHECK_NULL_RETURN(stackNode, nullptr);
192
193 auto columnNode = stackNode->GetLastChild();
194 CHECK_NULL_RETURN(columnNode, nullptr);
195
196 return DynamicCast<FrameNode>(columnNode);
197 }
198
GetColumnNodes()199 std::map<uint32_t, RefPtr<FrameNode>> TextPickerPattern::GetColumnNodes()
200 {
201 std::map<uint32_t, RefPtr<FrameNode>> allChildNode;
202 auto host = GetHost();
203 CHECK_NULL_RETURN(host, allChildNode);
204 auto children = host->GetChildren();
205 uint32_t index = 0;
206 for (auto iter = children.begin(); iter != children.end(); iter++) {
207 CHECK_NULL_RETURN(*iter, allChildNode);
208 auto stackNode = DynamicCast<FrameNode>(*iter);
209 auto currentNode = DynamicCast<FrameNode>(stackNode->GetLastChild());
210 allChildNode[index] = currentNode;
211 index++;
212 }
213 return allChildNode;
214 }
215
OnColumnsBuildingCascade()216 void TextPickerPattern::OnColumnsBuildingCascade()
217 {
218 auto frameNodes = GetColumnNodes();
219 auto count = frameNodes.size();
220 for (size_t index = 0; index < count; index++) {
221 CHECK_NULL_VOID(frameNodes[index]);
222 auto textPickerColumnPattern = frameNodes[index]->GetPattern<TextPickerColumnPattern>();
223 CHECK_NULL_VOID(textPickerColumnPattern);
224 if (cascadeOptions_.size() > 0) {
225 selectedIndex_ = cascadeOptions_[index].rangeResult.empty() ? 0 :
226 selecteds_[index] % cascadeOptions_[index].rangeResult.size();
227 textPickerColumnPattern->SetCurrentIndex(selectedIndex_);
228 std::vector<NG::RangeContent> rangeContents;
229 for (uint32_t i = 0; i < cascadeOptions_[index].rangeResult.size(); i++) {
230 NG::RangeContent rangeContent;
231 rangeContent.text_ = cascadeOptions_[index].rangeResult[i];
232 rangeContents.emplace_back(rangeContent);
233 }
234 textPickerColumnPattern->SetOptions(rangeContents);
235 textPickerColumnPattern->SetColumnKind(NG::TEXT);
236 optionsWithNode_[frameNodes[index]] = rangeContents;
237 frameNodes[index]->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
238 }
239 }
240 }
241
OnColumnsBuildingUnCascade()242 void TextPickerPattern::OnColumnsBuildingUnCascade()
243 {
244 auto frameNodes = GetColumnNodes();
245 for (auto it : frameNodes) {
246 CHECK_NULL_VOID(it.second);
247 auto textPickerColumnPattern = it.second->GetPattern<TextPickerColumnPattern>();
248 CHECK_NULL_VOID(textPickerColumnPattern);
249 if (cascadeOptions_.size() > 0) {
250 selectedIndex_ = cascadeOptions_[it.first].rangeResult.empty() ? 0 :
251 selecteds_[it.first] % cascadeOptions_[it.first].rangeResult.size();
252 textPickerColumnPattern->SetCurrentIndex(selectedIndex_);
253 std::vector<NG::RangeContent> rangeContents;
254 for (uint32_t i = 0; i < cascadeOptions_[it.first].rangeResult.size(); i++) {
255 NG::RangeContent rangeContent;
256 rangeContent.text_ = cascadeOptions_[it.first].rangeResult[i];
257 rangeContents.emplace_back(rangeContent);
258 }
259 textPickerColumnPattern->SetOptions(rangeContents);
260 textPickerColumnPattern->SetColumnKind(NG::TEXT);
261 optionsWithNode_[it.second] = rangeContents;
262 it.second->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
263 } else {
264 ClearOption();
265 for (const auto& item : range_) {
266 AppendOption(item);
267 }
268 selectedIndex_ = range_.empty() ? 0 : GetSelected() % range_.size();
269 textPickerColumnPattern->SetCurrentIndex(selectedIndex_);
270 textPickerColumnPattern->SetOptions(options_);
271 textPickerColumnPattern->SetColumnKind(columnsKind_);
272 it.second->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
273 }
274 }
275 }
276
OnColumnsBuilding()277 void TextPickerPattern::OnColumnsBuilding()
278 {
279 if (!isCascade_) {
280 OnColumnsBuildingUnCascade();
281 } else {
282 OnColumnsBuildingCascade();
283 }
284 }
285
SetSelecteds(const std::vector<uint32_t> & values)286 void TextPickerPattern::SetSelecteds(const std::vector<uint32_t>& values)
287 {
288 selecteds_.clear();
289 for (auto& value : values) {
290 selecteds_.emplace_back(value);
291 }
292 if (isCascade_) {
293 auto columnCount = cascadeOptions_.size();
294 cascadeOptions_.clear();
295 ProcessCascadeOptions(cascadeOriginptions_, cascadeOptions_, 0);
296 if (cascadeOptions_.size() < columnCount) {
297 auto differ = columnCount - cascadeOptions_.size();
298 for (uint32_t i = 0; i < differ; i++) {
299 NG::TextCascadePickerOptions differOption;
300 memset_s(&differOption, sizeof(differOption), 0, sizeof(differOption));
301 cascadeOptions_.emplace_back(differOption);
302 }
303 }
304 }
305 }
306
FlushOptions()307 void TextPickerPattern::FlushOptions()
308 {
309 auto frameNodes = GetColumnNodes();
310 for (auto it : frameNodes) {
311 CHECK_NULL_VOID(it.second);
312 auto columnPattern = it.second->GetPattern<TextPickerColumnPattern>();
313 CHECK_NULL_VOID(columnPattern);
314 columnPattern->FlushCurrentOptions();
315 it.second->MarkModifyDone();
316 it.second->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
317 }
318 }
319
CalculateHeight()320 double TextPickerPattern::CalculateHeight()
321 {
322 double height = 0.0;
323 auto host = GetHost();
324 CHECK_NULL_RETURN(host, height);
325 auto textPickerLayoutProperty = host->GetLayoutProperty<TextPickerLayoutProperty>();
326 CHECK_NULL_RETURN(textPickerLayoutProperty, height);
327 auto context = host->GetContext();
328 CHECK_NULL_RETURN(context, height);
329 auto pickerTheme = context->GetTheme<PickerTheme>();
330 CHECK_NULL_RETURN(pickerTheme, height);
331 if (textPickerLayoutProperty->HasDefaultPickerItemHeight()) {
332 auto defaultPickerItemHeightValue = textPickerLayoutProperty->GetDefaultPickerItemHeightValue();
333 if (context->NormalizeToPx(defaultPickerItemHeightValue) <= 0) {
334 height = pickerTheme->GetDividerSpacing().ConvertToPx();
335 return height;
336 }
337 if (defaultPickerItemHeightValue.Unit() == DimensionUnit::PERCENT) {
338 height = pickerTheme->GetGradientHeight().ConvertToPx() * defaultPickerItemHeightValue.Value();
339 } else {
340 height = context->NormalizeToPx(defaultPickerItemHeightValue);
341 }
342 } else {
343 height = pickerTheme->GetDividerSpacing().ConvertToPx();
344 }
345 if (defaultPickerItemHeight_ != height) {
346 defaultPickerItemHeight_ = height;
347 PaintFocusState();
348 SetButtonIdeaSize();
349 }
350 auto frameNodes = GetColumnNodes();
351 for (auto it : frameNodes) {
352 CHECK_NULL_RETURN(it.second, height);
353 auto textPickerColumnPattern = it.second->GetPattern<TextPickerColumnPattern>();
354 CHECK_NULL_RETURN(textPickerColumnPattern, height);
355 textPickerColumnPattern->SetDefaultPickerItemHeight(height);
356 }
357 return height;
358 }
359
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)360 void TextPickerPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
361 {
362 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
363 auto pattern = wp.Upgrade();
364 CHECK_NULL_RETURN_NOLOG(pattern, false);
365 return pattern->OnKeyEvent(event);
366 };
367 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
368
369 auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
370 auto pattern = wp.Upgrade();
371 if (pattern) {
372 pattern->GetInnerFocusPaintRect(paintRect);
373 }
374 };
375 focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
376 }
377
PaintFocusState()378 void TextPickerPattern::PaintFocusState()
379 {
380 auto host = GetHost();
381 CHECK_NULL_VOID(host);
382
383 RoundRect focusRect;
384 GetInnerFocusPaintRect(focusRect);
385 auto focusHub = host->GetFocusHub();
386 CHECK_NULL_VOID(focusHub);
387 focusHub->PaintInnerFocusState(focusRect);
388
389 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
390 }
391
SetFocusCornerRadius(RoundRect & paintRect)392 void TextPickerPattern::SetFocusCornerRadius(RoundRect& paintRect)
393 {
394 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
395 static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
396 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
397 static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
398 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
399 static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
400 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS, static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()),
401 static_cast<RSScalar>(PRESS_RADIUS.ConvertToPx()));
402 }
403
GetInnerFocusPaintRect(RoundRect & paintRect)404 void TextPickerPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
405 {
406 auto host = GetHost();
407 CHECK_NULL_VOID(host);
408 auto childSize = host->GetChildren().size();
409 if (childSize == 0) {
410 return;
411 }
412 auto columnNode = GetColumnNode();
413 CHECK_NULL_VOID(columnNode);
414 auto pipeline = PipelineBase::GetCurrentContext();
415 CHECK_NULL_VOID(pipeline);
416 auto pickerTheme = pipeline->GetTheme<PickerTheme>();
417 CHECK_NULL_VOID(pickerTheme);
418 auto stackChild = DynamicCast<FrameNode>(host->GetChildAtIndex(focusKeyID_));
419 CHECK_NULL_VOID(stackChild);
420 auto pickerChild = DynamicCast<FrameNode>(stackChild->GetLastChild());
421 CHECK_NULL_VOID(pickerChild);
422 auto columnWidth = pickerChild->GetGeometryNode()->GetFrameSize().Width();
423 auto frameWidth = host->GetGeometryNode()->GetFrameSize().Width();
424 auto dividerSpacing = pipeline->NormalizeToPx(pickerTheme->GetDividerSpacing());
425 auto pickerThemeWidth = dividerSpacing * RATE;
426
427 auto centerX = (frameWidth / childSize - pickerThemeWidth) / RATE +
428 columnNode->GetGeometryNode()->GetFrameRect().Width() * focusKeyID_ +
429 PRESS_INTERVAL.ConvertToPx() * RATE;
430 auto centerY =
431 (host->GetGeometryNode()->GetFrameSize().Height() - dividerSpacing) / RATE + PRESS_INTERVAL.ConvertToPx();
432 float piantRectWidth = (dividerSpacing - PRESS_INTERVAL.ConvertToPx()) * RATE;
433 float piantRectHeight = dividerSpacing - PRESS_INTERVAL.ConvertToPx() * RATE;
434 if (!GetIsShowInDialog()) {
435 piantRectHeight = piantRectHeight - OFFSET_LENGTH.ConvertToPx();
436 centerY = centerY + OFFSET.ConvertToPx();
437 } else {
438 piantRectHeight = piantRectHeight - DIALOG_OFFSET.ConvertToPx();
439 centerY = centerY + DIALOG_OFFSET_LENGTH.ConvertToPx();
440 centerX = centerX + FOUCS_WIDTH.ConvertToPx();
441 }
442 if (piantRectWidth > columnWidth) {
443 if (!GetIsShowInDialog()) {
444 piantRectWidth = columnWidth - FOUCS_WIDTH.ConvertToPx() - PRESS_INTERVAL.ConvertToPx();
445 centerX = focusKeyID_ * (piantRectWidth + FOUCS_WIDTH.ConvertToPx() + PRESS_INTERVAL.ConvertToPx()) +
446 FOUCS_WIDTH.ConvertToPx();
447 } else {
448 piantRectWidth = columnWidth - FOUCS_WIDTH.ConvertToPx();
449 centerX = focusKeyID_ * piantRectWidth + FOUCS_WIDTH.ConvertToPx() / HALF;
450 }
451 } else {
452 centerX = centerX - MARGIN_SIZE.ConvertToPx() / HALF;
453 }
454 paintRect.SetRect(RectF(centerX, centerY, piantRectWidth, piantRectHeight));
455 SetFocusCornerRadius(paintRect);
456 }
457
OnKeyEvent(const KeyEvent & event)458 bool TextPickerPattern::OnKeyEvent(const KeyEvent& event)
459 {
460 if (event.action != KeyAction::DOWN) {
461 return false;
462 }
463
464 if (event.code == KeyCode::KEY_SPACE || event.code == KeyCode::KEY_ENTER) {
465 if (!operationOn_ && event.code == KeyCode::KEY_ENTER) {
466 HandleDirectionKey(event.code);
467 }
468 operationOn_ = (event.code == KeyCode::KEY_SPACE) || (event.code == KeyCode::KEY_ENTER);
469 return true;
470 }
471
472 if (event.code == KeyCode::KEY_TAB) {
473 operationOn_ = false;
474 return false;
475 }
476
477 if (event.code == KeyCode::KEY_DPAD_UP || event.code == KeyCode::KEY_DPAD_DOWN ||
478 event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_DPAD_RIGHT) {
479 if (operationOn_) {
480 HandleDirectionKey(event.code);
481 }
482 return true;
483 }
484 return false;
485 }
486
SetChangeCallback(ColumnChangeCallback && value)487 void TextPickerPattern::SetChangeCallback(ColumnChangeCallback&& value)
488 {
489 auto host = GetHost();
490 CHECK_NULL_VOID(host);
491 auto children = host->GetChildren();
492 for (const auto& child : children) {
493 auto stackNode = DynamicCast<FrameNode>(child);
494 CHECK_NULL_VOID(stackNode);
495 auto childNode = DynamicCast<FrameNode>(stackNode->GetLastChild());
496 CHECK_NULL_VOID(childNode);
497 auto textPickerColumnPattern = childNode->GetPattern<TextPickerColumnPattern>();
498 CHECK_NULL_VOID(textPickerColumnPattern);
499 textPickerColumnPattern->SetChangeCallback(std::move(value));
500 }
501 }
502
ProcessCascadeOptionDepth(const NG::TextCascadePickerOptions & option)503 size_t TextPickerPattern::ProcessCascadeOptionDepth(const NG::TextCascadePickerOptions& option)
504 {
505 size_t depth = 1;
506 if (option.children.empty()) {
507 return depth;
508 }
509
510 for (auto& pos : option.children) {
511 size_t tmpDep = 1;
512 tmpDep += ProcessCascadeOptionDepth(pos);
513 if (tmpDep > depth) {
514 depth = tmpDep;
515 }
516 }
517 return depth;
518 }
519
ChangeCurrentOptionValue(NG::TextCascadePickerOptions & option,uint32_t value,uint32_t curColumn,uint32_t replaceColumn)520 bool TextPickerPattern::ChangeCurrentOptionValue(NG::TextCascadePickerOptions& option,
521 uint32_t value, uint32_t curColumn, uint32_t replaceColumn)
522 {
523 if (curColumn >= replaceColumn) {
524 selecteds_[curColumn] = value;
525 values_[curColumn] = "";
526 }
527
528 for (uint32_t valueIndex = 0; valueIndex < option.children.size(); valueIndex++) {
529 if (curColumn >= replaceColumn) {
530 if (ChangeCurrentOptionValue(option.children[valueIndex], 0, curColumn + 1, replaceColumn)) {
531 return true;
532 }
533 } else {
534 if (ChangeCurrentOptionValue(option.children[valueIndex], value, curColumn + 1, replaceColumn)) {
535 return true;
536 }
537 }
538 }
539 return false;
540 }
541
ProcessCascadeOptionsValues(const std::vector<std::string> & rangeResultValue,uint32_t index)542 void TextPickerPattern::ProcessCascadeOptionsValues(const std::vector<std::string>& rangeResultValue,
543 uint32_t index)
544 {
545 auto valueIterator = std::find(rangeResultValue.begin(), rangeResultValue.end(), values_[index]);
546 if (valueIterator != rangeResultValue.end()) {
547 if (index < selecteds_.size()) {
548 selecteds_[index] = std::distance(rangeResultValue.begin(), valueIterator);
549 } else {
550 selecteds_.emplace_back(std::distance(rangeResultValue.begin(), valueIterator));
551 }
552 } else {
553 if (index < selecteds_.size()) {
554 selecteds_[index] = 0;
555 } else {
556 selecteds_.emplace_back(0);
557 }
558 }
559 }
560
ProcessCascadeOptions(const std::vector<NG::TextCascadePickerOptions> & options,std::vector<NG::TextCascadePickerOptions> & reOptions,uint32_t index)561 void TextPickerPattern::ProcessCascadeOptions(const std::vector<NG::TextCascadePickerOptions>& options,
562 std::vector<NG::TextCascadePickerOptions>& reOptions, uint32_t index)
563 {
564 std::vector<std::string> rangeResultValue;
565 NG::TextCascadePickerOptions option;
566 for (size_t i = 0; i < options.size(); i++) {
567 if (!options[i].rangeResult.empty()) {
568 rangeResultValue.emplace_back(options[i].rangeResult[0]);
569 }
570 }
571 option.rangeResult = rangeResultValue;
572 for (size_t i = 0; i < options.size(); i++) {
573 if (index < selecteds_.size() &&
574 ((selecteds_[index] != 0 && !isHasSelectAttr_) || isHasSelectAttr_)) {
575 if (selecteds_[index] < 0 && selecteds_[index] > options.size()) {
576 selecteds_[index] = 0;
577 }
578 option.children = options[selecteds_[index]].children;
579 reOptions.emplace_back(option);
580 return ProcessCascadeOptions(options[selecteds_[index]].children, reOptions, index + 1);
581 }
582 if (index < values_.size() && values_[index] != "") {
583 ProcessCascadeOptionsValues(rangeResultValue, index);
584 option.children = options[selecteds_[index]].children;
585 reOptions.emplace_back(option);
586 return ProcessCascadeOptions(options[selecteds_[index]].children, reOptions, index + 1);
587 }
588 if (i == options.size() - 1) {
589 option.children = options[0].children;
590 reOptions.emplace_back(option);
591 return ProcessCascadeOptions(options[0].children, reOptions, index + 1);
592 }
593 }
594 }
595
SupplementOption(const std::vector<NG::TextCascadePickerOptions> & reOptions,std::vector<NG::RangeContent> & rangeContents,uint32_t patterIndex)596 void TextPickerPattern::SupplementOption(const std::vector<NG::TextCascadePickerOptions>& reOptions,
597 std::vector<NG::RangeContent>& rangeContents, uint32_t patterIndex)
598 {
599 for (uint32_t i = 0; i < reOptions[patterIndex].rangeResult.size(); i++) {
600 NG::RangeContent rangeContent;
601 rangeContent.text_ = reOptions[patterIndex].rangeResult[i];
602 rangeContents.emplace_back(rangeContent);
603 }
604 }
605
HandleColumnChange(const RefPtr<FrameNode> & tag,bool isAdd,uint32_t index,bool needNotify)606 void TextPickerPattern::HandleColumnChange(const RefPtr<FrameNode>& tag, bool isAdd,
607 uint32_t index, bool needNotify)
608 {
609 if (isCascade_) {
610 auto frameNodes = GetColumnNodes();
611 auto columnIndex = 0;
612 for (auto iter = frameNodes.begin(); iter != frameNodes.end(); iter++) {
613 if (iter->second->GetId() == tag->GetId()) {
614 break;
615 }
616 columnIndex++;
617 }
618 for (uint32_t valueIndex = 0; valueIndex < cascadeOriginptions_.size(); valueIndex++) {
619 ChangeCurrentOptionValue(cascadeOriginptions_[valueIndex], index, 0, columnIndex);
620 }
621
622 std::vector<NG::TextCascadePickerOptions> reOptions;
623 ProcessCascadeOptions(cascadeOriginptions_, reOptions, 0);
624 // Next Column Update Value
625 columnIndex = columnIndex + 1;
626 for (uint32_t patterIndex = columnIndex; patterIndex < frameNodes.size(); patterIndex++) {
627 auto patternNode = frameNodes[patterIndex];
628 CHECK_NULL_VOID(patternNode);
629 auto textPickerColumnPattern = patternNode->GetPattern<TextPickerColumnPattern>();
630 CHECK_NULL_VOID(textPickerColumnPattern);
631 if (patterIndex < reOptions.size()) {
632 auto currentSelectedIndex = reOptions[patterIndex].rangeResult.empty() ? 0 :
633 selecteds_[patterIndex] % reOptions[patterIndex].rangeResult.size();
634 std::vector<NG::RangeContent> rangeContents;
635 SupplementOption(reOptions, rangeContents, patterIndex);
636 textPickerColumnPattern->SetCurrentIndex(currentSelectedIndex);
637 textPickerColumnPattern->SetOptions(rangeContents);
638 textPickerColumnPattern->FlushCurrentOptions();
639 } else {
640 textPickerColumnPattern->ClearOptions();
641 textPickerColumnPattern->SetCurrentIndex(0);
642 textPickerColumnPattern->FlushCurrentOptions(false, false, true);
643 }
644 }
645 }
646 }
647
HandleDirectionKey(KeyCode code)648 bool TextPickerPattern::HandleDirectionKey(KeyCode code)
649 {
650 auto host = GetHost();
651 CHECK_NULL_RETURN(host, false);
652 auto childSize = host->GetChildren().size();
653 auto frameNode = GetColumnNode();
654 CHECK_NULL_RETURN(frameNode, false);
655 auto textPickerColumnPattern = frameNode->GetPattern<TextPickerColumnPattern>();
656 CHECK_NULL_RETURN(textPickerColumnPattern, false);
657 auto totalOptionCount = textPickerColumnPattern->GetOptionCount();
658 if (totalOptionCount == 0) {
659 return false;
660 }
661 bool result = true;
662 switch (code) {
663 case KeyCode::KEY_DPAD_UP:
664 textPickerColumnPattern->InnerHandleScroll(-1, false);
665 break;
666
667 case KeyCode::KEY_DPAD_DOWN:
668 textPickerColumnPattern->InnerHandleScroll(1, false);
669 break;
670
671 case KeyCode::KEY_ENTER:
672 focusKeyID_ = 0;
673 PaintFocusState();
674 break;
675
676 case KeyCode::KEY_DPAD_LEFT:
677 focusKeyID_ -= 1;
678 if (focusKeyID_ < 0) {
679 focusKeyID_ = 0;
680 }
681 PaintFocusState();
682 break;
683
684 case KeyCode::KEY_DPAD_RIGHT:
685 focusKeyID_ += 1;
686 if (focusKeyID_ > static_cast<int32_t>(childSize) - 1) {
687 focusKeyID_ = static_cast<int32_t>(childSize) - 1;
688 }
689 PaintFocusState();
690 break;
691
692 default:
693 result = false;
694 break;
695 }
696
697 return result;
698 }
699
GetSelectedObjectMulti(const std::vector<std::string> & values,const std::vector<uint32_t> & indexs,int32_t status) const700 std::string TextPickerPattern::GetSelectedObjectMulti(const std::vector<std::string>& values,
701 const std::vector<uint32_t>& indexs, int32_t status) const
702 {
703 std::string result = "";
704 result = std::string("{\"value\":") + "[";
705 for (uint32_t i = 0; i < values.size(); i++) {
706 result += "\"" + values[i];
707 if (i != values.size() - 1) {
708 result += "\",";
709 } else {
710 result += "\"]";
711 }
712 }
713 result += std::string(",\"index\":") + "[";
714 for (uint32_t i = 0; i < indexs.size(); i++) {
715 result += "\"" + std::to_string(indexs[i]);
716 if (i != indexs.size() - 1) {
717 result += "\",";
718 } else {
719 result += "\"]";
720 }
721 }
722 result += ",\"status\":" + std::to_string(status) + "}";
723 return result;
724 }
725
GetSelectedObject(bool isColumnChange,int32_t status) const726 std::string TextPickerPattern::GetSelectedObject(bool isColumnChange, int32_t status) const
727 {
728 std::vector<std::string> values;
729 std::vector<uint32_t> indexs;
730 auto host = GetHost();
731 CHECK_NULL_RETURN(host, "");
732 auto children = host->GetChildren();
733 for (const auto& child : children) {
734 CHECK_NULL_RETURN(child, "");
735 auto stackNode = DynamicCast<FrameNode>(child);
736 CHECK_NULL_RETURN(stackNode, "");
737 auto currentNode = DynamicCast<FrameNode>(stackNode->GetLastChild());
738 CHECK_NULL_RETURN(currentNode, "");
739 auto textPickerColumnPattern = currentNode->GetPattern<TextPickerColumnPattern>();
740 CHECK_NULL_RETURN(textPickerColumnPattern, "");
741 auto value = textPickerColumnPattern->GetOption(textPickerColumnPattern->GetSelected());
742 auto index = textPickerColumnPattern->GetSelected();
743 if (isColumnChange) {
744 value = textPickerColumnPattern->GetCurrentText();
745 index = textPickerColumnPattern->GetCurrentIndex();
746 }
747 values.emplace_back(value);
748 indexs.emplace_back(index);
749 }
750
751 auto context = host->GetContext();
752 CHECK_NULL_RETURN(context, "");
753 if (context->GetIsDeclarative()) {
754 if (values.size() == 1) {
755 return std::string("{\"value\":") + "\"" + values[0] + "\"" + ",\"index\":" + std::to_string(indexs[0]) +
756 ",\"status\":" + std::to_string(status) + "}";
757 } else {
758 return GetSelectedObjectMulti(values, indexs, status);
759 }
760 } else {
761 return std::string("{\"newValue\":") + "\"" +
762 values[0] + "\"" + ",\"newSelected\":" + std::to_string(indexs[0]) +
763 ",\"status\":" + std::to_string(status) + "}";
764 }
765 }
766
ToJsonValue(std::unique_ptr<JsonValue> & json) const767 void TextPickerPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
768 {
769 if (!range_.empty()) {
770 json->Put("range", GetRangeStr().c_str());
771 } else {
772 if (!cascadeOriginptions_.empty()) {
773 if (!isCascade_) {
774 json->Put("range", GetOptionsMultiStr().c_str());
775 } else {
776 json->Put("range", GetOptionsCascadeStr(cascadeOriginptions_).c_str());
777 }
778 }
779 }
780 }
781
GetRangeStr() const782 std::string TextPickerPattern::GetRangeStr() const
783 {
784 if (!range_.empty()) {
785 std::string result = "[";
786 for (const auto& item : range_) {
787 result += "\"";
788 result += "icon:";
789 result += item.icon_;
790 result += ",";
791 result += "text:";
792 result += item.text_;
793 result += "\"";
794 result += ",";
795 }
796 result.pop_back();
797 result += "]";
798 return result;
799 }
800 return "";
801 }
802
GetOptionsCascadeStr(const std::vector<NG::TextCascadePickerOptions> & options) const803 std::string TextPickerPattern::GetOptionsCascadeStr(
804 const std::vector<NG::TextCascadePickerOptions>& options) const
805 {
806 std::string result = "[";
807 for (uint32_t i = 0; i < options.size(); i++) {
808 result += std::string("{\"text\":\"");
809 result += options[i].rangeResult[0];
810 result += "\"";
811 if (options[i].children.size() > 0) {
812 result += std::string(", \"children\":");
813 result += GetOptionsCascadeStr(options[i].children);
814 }
815 if (i != options.size() - 1) {
816 result += "},";
817 } else {
818 result += "}]";
819 }
820 }
821 return result;
822 }
823
GetOptionsMultiStrInternal() const824 std::string TextPickerPattern::GetOptionsMultiStrInternal() const
825 {
826 std::string result = "[";
827 for (uint32_t i = 0; i < cascadeOptions_.size(); i++) {
828 result += "[";
829 for (uint32_t j = 0; j < cascadeOptions_[i].rangeResult.size(); j++) {
830 result += "\"" + cascadeOptions_[i].rangeResult[j];
831 if (j != cascadeOptions_[i].rangeResult.size() - 1) {
832 result += "\",";
833 } else {
834 result += "\"]";
835 }
836 }
837 if (i != cascadeOptions_.size() - 1) {
838 result += ",";
839 } else {
840 result += "]";
841 }
842 }
843 return result;
844 }
845
GetOptionsMultiStr() const846 std::string TextPickerPattern::GetOptionsMultiStr() const
847 {
848 std::string result = "";
849 if (!cascadeOptions_.empty()) {
850 result = GetOptionsMultiStrInternal();
851 }
852 return result;
853 }
854
OnColorConfigurationUpdate()855 void TextPickerPattern::OnColorConfigurationUpdate()
856 {
857 auto host = GetHost();
858 CHECK_NULL_VOID(host);
859 host->SetNeedCallChildrenUpdate(false);
860 auto context = host->GetContext();
861 CHECK_NULL_VOID(context);
862 auto pickerTheme = context->GetTheme<PickerTheme>();
863 CHECK_NULL_VOID(pickerTheme);
864 auto dialogTheme = context->GetTheme<DialogTheme>();
865 CHECK_NULL_VOID(dialogTheme);
866 auto disappearStyle = pickerTheme->GetDisappearOptionStyle();
867 auto normalStyle = pickerTheme->GetOptionStyle(false, false);
868 auto pickerProperty = host->GetLayoutProperty<TextPickerLayoutProperty>();
869 CHECK_NULL_VOID(pickerProperty);
870 pickerProperty->UpdateColor(normalStyle.GetTextColor());
871 pickerProperty->UpdateDisappearColor(disappearStyle.GetTextColor());
872 if (isPicker_) {
873 return;
874 }
875 SetBackgroundColor(dialogTheme->GetBackgroundColor());
876 if (contentRowNode_) {
877 auto layoutRenderContext = contentRowNode_->GetRenderContext();
878 CHECK_NULL_VOID(layoutRenderContext);
879 layoutRenderContext->UpdateBackgroundColor(dialogTheme->GetButtonBackgroundColor());
880 }
881 auto frameNode = DynamicCast<FrameNode>(host);
882 CHECK_NULL_VOID(frameNode);
883 FrameNode::ProcessOffscreenNode(frameNode);
884 host->MarkModifyDone();
885 }
886 } // namespace OHOS::Ace::NG
887