• 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_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