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 #include "core/components_ng/pattern/option/option_view.h"
16 
17 #include "base/geometry/dimension.h"
18 #include "base/i18n/localization.h"
19 #include "base/memory/ace_type.h"
20 #include "base/memory/referenced.h"
21 #include "base/utils/utils.h"
22 #include "core/components/select/select_theme.h"
23 #include "core/components_ng/base/frame_node.h"
24 #include "core/components_ng/base/view_stack_processor.h"
25 #include "core/components_ng/pattern/image/image_model_ng.h"
26 #include "core/components_ng/pattern/image/image_pattern.h"
27 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
28 #include "core/components_ng/pattern/option/option_event_hub.h"
29 #include "core/components_ng/pattern/option/option_pattern.h"
30 #include "core/components_ng/pattern/security_component/paste_button/paste_button_common.h"
31 #include "core/components_ng/pattern/security_component/paste_button/paste_button_model_ng.h"
32 #include "core/components_ng/pattern/security_component/security_component_pattern.h"
33 #include "core/components_ng/pattern/text/text_pattern.h"
34 #include "core/components_v2/inspector/inspector_constants.h"
35 #include "core/image/image_source_info.h"
36 
37 namespace OHOS::Ace::NG {
38 
39 namespace {
40 
Create(int32_t index)41 RefPtr<FrameNode> Create(int32_t index)
42 {
43     auto Id = ElementRegister::GetInstance()->MakeUniqueId();
44     ACE_LAYOUT_SCOPED_TRACE("Create[%s][self:%d]", V2::OPTION_ETS_TAG, Id);
45     auto node = FrameNode::CreateFrameNode(V2::OPTION_ETS_TAG, Id, AceType::MakeRefPtr<OptionPattern>(index));
46 
47     // set border radius
48     auto renderContext = node->GetRenderContext();
49     CHECK_NULL_RETURN(renderContext, nullptr);
50     auto pipeline = PipelineBase::GetCurrentContext();
51     CHECK_NULL_RETURN(pipeline, nullptr);
52     auto theme = pipeline->GetTheme<SelectTheme>();
53     CHECK_NULL_RETURN(theme, nullptr);
54     BorderRadiusProperty border;
55     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
56         border.SetRadius(theme->GetMenuDefaultInnerRadius());
57     } else {
58         border.SetRadius(theme->GetInnerBorderRadius());
59     }
60     renderContext->UpdateBorderRadius(border);
61 
62     auto props = node->GetPaintProperty<OptionPaintProperty>();
63     CHECK_NULL_RETURN(props, nullptr);
64     props->UpdateHover(false);
65     props->UpdatePress(false);
66     return node;
67 }
68 } // namespace
69 
CreateText(const std::string & value,const RefPtr<FrameNode> & parent)70 RefPtr<FrameNode> OptionView::CreateText(const std::string& value, const RefPtr<FrameNode>& parent)
71 {
72     // create child text node
73     auto textId = ElementRegister::GetInstance()->MakeUniqueId();
74     auto textNode = FrameNode::CreateFrameNode(V2::TEXT_ETS_TAG, textId, AceType::MakeRefPtr<TextPattern>());
75     CHECK_NULL_RETURN(textNode, nullptr);
76 
77     auto textProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
78     CHECK_NULL_RETURN(textProperty, nullptr);
79 
80     auto pipeline = PipelineBase::GetCurrentContext();
81     CHECK_NULL_RETURN(pipeline, nullptr);
82     auto theme = pipeline->GetTheme<SelectTheme>();
83     CHECK_NULL_RETURN(theme, nullptr);
84 
85     textProperty->UpdateMaxLines(1);
86     textProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
87     textProperty->UpdateFontSize(theme->GetMenuFontSize());
88     textProperty->UpdateFontWeight(FontWeight::REGULAR);
89     textProperty->UpdateTextColor(theme->GetMenuFontColor());
90     // set default foregroundColor
91     auto textRenderContext = textNode->GetRenderContext();
92     textRenderContext->UpdateForegroundColor(theme->GetMenuFontColor());
93     textProperty->UpdateContent(value);
94     textNode->MountToParent(parent);
95     textNode->MarkModifyDone();
96 
97     return textNode;
98 }
99 
CreateIcon(const std::string & icon,const RefPtr<FrameNode> & parent,const RefPtr<FrameNode> & child)100 RefPtr<FrameNode> OptionView::CreateIcon(const std::string& icon, const RefPtr<FrameNode>& parent,
101     const RefPtr<FrameNode>& child)
102 {
103     auto iconNode = FrameNode::CreateFrameNode(
104         V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
105     CHECK_NULL_RETURN(iconNode, nullptr);
106     auto props = iconNode->GetLayoutProperty<ImageLayoutProperty>();
107     auto pipeline = PipelineBase::GetCurrentContext();
108     CHECK_NULL_RETURN(pipeline, nullptr);
109     auto theme = pipeline->GetTheme<SelectTheme>();
110     CHECK_NULL_RETURN(theme, nullptr);
111     if (!icon.empty()) {
112         ImageSourceInfo info(icon);
113         props->UpdateImageSourceInfo(info);
114     }
115     props->UpdateUserDefinedIdealSize(
116         CalcSize(CalcLength(theme->GetIconSideLength()), CalcLength(theme->GetIconSideLength())));
117     props->UpdateAlignment(Alignment::CENTER_LEFT);
118 
119     if (child) {
120         parent->ReplaceChild(child, iconNode);
121     } else {
122         iconNode->MountToParent(parent, 0);
123     }
124     iconNode->MarkModifyDone();
125     return iconNode;
126 }
127 
CreateSymbol(const std::function<void (WeakPtr<NG::FrameNode>)> & symbolApply,const RefPtr<FrameNode> & parent,const RefPtr<FrameNode> & child,const std::optional<Dimension> & symbolUserDefinedIdealFontSize)128 RefPtr<FrameNode> OptionView::CreateSymbol(const std::function<void(WeakPtr<NG::FrameNode>)>& symbolApply,
129     const RefPtr<FrameNode>& parent, const RefPtr<FrameNode>& child,
130     const std::optional<Dimension>& symbolUserDefinedIdealFontSize)
131 {
132     auto iconNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
133         []() { return AceType::MakeRefPtr<TextPattern>(); });
134     CHECK_NULL_RETURN(iconNode, nullptr);
135     auto props = iconNode->GetLayoutProperty<TextLayoutProperty>();
136     CHECK_NULL_RETURN(props, nullptr);
137     auto pipeline = PipelineBase::GetCurrentContext();
138     CHECK_NULL_RETURN(pipeline, nullptr);
139     auto theme = pipeline->GetTheme<SelectTheme>();
140     CHECK_NULL_RETURN(theme, nullptr);
141     props->UpdateFontSize(theme->GetEndIconWidth());
142     props->UpdateSymbolColorList({theme->GetMenuIconColor()});
143     props->UpdateAlignment(Alignment::CENTER_LEFT);
144     MarginProperty margin;
145     margin.right = CalcLength(theme->GetIconContentPadding());
146     props->UpdateMargin(margin);
147     if (symbolApply != nullptr) {
148         symbolApply(AccessibilityManager::WeakClaim(AccessibilityManager::RawPtr(iconNode)));
149     }
150     if (symbolUserDefinedIdealFontSize.has_value()) {
151         props->UpdateFontSize(symbolUserDefinedIdealFontSize.value());
152     }
153     if (child) {
154         parent->ReplaceChild(child, iconNode);
155     } else {
156         iconNode->MountToParent(parent, 0);
157     }
158     iconNode->MarkModifyDone();
159     return iconNode;
160 }
161 
CreatePasteButton(bool optionsHasIcon,const RefPtr<FrameNode> & option,const RefPtr<FrameNode> & row,const std::function<void ()> & onClickFunc,const std::string & icon)162 void OptionView::CreatePasteButton(bool optionsHasIcon, const RefPtr<FrameNode>& option, const RefPtr<FrameNode>& row,
163     const std::function<void()>& onClickFunc, const std::string& icon)
164 {
165     RefPtr<FrameNode> pasteNode;
166     if (optionsHasIcon) {
167         pasteNode =
168             PasteButtonModelNG::GetInstance()->CreateNode(static_cast<int32_t>(PasteButtonPasteDescription::PASTE),
169                 static_cast<int32_t>(PasteButtonIconStyle::ICON_LINE), static_cast<int32_t>(ButtonType::NORMAL), true);
170     } else {
171         pasteNode =
172             PasteButtonModelNG::GetInstance()->CreateNode(static_cast<int32_t>(PasteButtonPasteDescription::PASTE),
173                 static_cast<int32_t>(PasteButtonIconStyle::ICON_NULL), static_cast<int32_t>(ButtonType::NORMAL), true);
174     }
175     CHECK_NULL_VOID(pasteNode);
176     auto pattern = option->GetPattern<OptionPattern>();
177     CHECK_NULL_VOID(pattern);
178 
179     auto pasteLayoutProperty = pasteNode->GetLayoutProperty<SecurityComponentLayoutProperty>();
180     CHECK_NULL_VOID(pasteLayoutProperty);
181     auto pastePaintProperty = pasteNode->GetPaintProperty<SecurityComponentPaintProperty>();
182     CHECK_NULL_VOID(pastePaintProperty);
183     auto pipeline = PipelineBase::GetCurrentContext();
184     CHECK_NULL_VOID(pipeline);
185     auto theme = pipeline->GetTheme<SelectTheme>();
186     CHECK_NULL_VOID(theme);
187 
188     pasteLayoutProperty->UpdateFontSize(theme->GetMenuFontSize());
189     pasteLayoutProperty->UpdateFontWeight(FontWeight::REGULAR);
190     pastePaintProperty->UpdateFontColor(theme->GetMenuFontColor());
191     pastePaintProperty->UpdateBackgroundColor(Color::TRANSPARENT);
192     pasteLayoutProperty->UpdateBackgroundBorderRadius(theme->GetInnerBorderRadius());
193     pasteLayoutProperty->UpdateIconSize(theme->GetIconSideLength());
194     pastePaintProperty->UpdateIconColor(theme->GetMenuIconColor());
195     if (optionsHasIcon) {
196         pasteLayoutProperty->UpdateTextIconSpace(theme->GetIconContentPadding());
197     }
198     pasteNode->MountToParent(row);
199     pasteNode->MarkModifyDone();
200 
201     row->MountToParent(option);
202     row->MarkModifyDone();
203     auto eventHub = option->GetEventHub<OptionEventHub>();
204     CHECK_NULL_VOID(eventHub);
205     pasteNode->GetOrCreateGestureEventHub()->SetUserOnClick([onClickFunc](GestureEvent& info) {
206         if (!PasteButtonModelNG::GetInstance()->IsClickResultSuccess(info)) {
207             return;
208         }
209         if (onClickFunc) {
210             onClickFunc();
211         }
212     });
213     pattern->SetPasteButton(pasteNode);
214 }
215 
CreateOption(bool optionsHasIcon,std::vector<OptionParam> & params,int32_t index,const RefPtr<FrameNode> & row,const RefPtr<FrameNode> & option)216 void OptionView::CreateOption(bool optionsHasIcon, std::vector<OptionParam>& params, int32_t index,
217     const RefPtr<FrameNode>& row, const RefPtr<FrameNode>& option)
218 {
219     auto pattern = option->GetPattern<OptionPattern>();
220     CHECK_NULL_VOID(pattern);
221     if (optionsHasIcon) {
222         auto iconNode = CreateSymbol(params[index].symbol, row, nullptr, params[index].symbolUserDefinedIdealFontSize);
223         pattern->SetIconNode(iconNode);
224     }
225     auto textNode = CreateText(params[index].value, row);
226     row->MountToParent(option);
227     row->MarkModifyDone();
228     pattern->SetTextNode(textNode);
229 
230     auto eventHub = option->GetEventHub<OptionEventHub>();
231     CHECK_NULL_VOID(eventHub);
232     eventHub->SetMenuOnClick(params[index].action);
233 }
234 
CreateOption(bool optionsHasIcon,const std::string & value,const std::string & icon,const RefPtr<FrameNode> & row,const RefPtr<FrameNode> & option,const std::function<void ()> & onClickFunc)235 void OptionView::CreateOption(bool optionsHasIcon, const std::string& value, const std::string& icon,
236     const RefPtr<FrameNode>& row, const RefPtr<FrameNode>& option, const std::function<void()>& onClickFunc)
237 {
238     auto pattern = option->GetPattern<OptionPattern>();
239     CHECK_NULL_VOID(pattern);
240     if (optionsHasIcon) {
241         auto iconNode = CreateIcon(icon, row);
242         pattern->SetIconNode(iconNode);
243         pattern->SetIcon(icon);
244     }
245     auto textNode = CreateText(value, row);
246     row->MountToParent(option);
247     row->MarkModifyDone();
248     pattern->SetTextNode(textNode);
249 
250     auto eventHub = option->GetEventHub<OptionEventHub>();
251     CHECK_NULL_VOID(eventHub);
252     eventHub->SetMenuOnClick(onClickFunc);
253 }
254 
CreateMenuOption(bool optionsHasIcon,std::vector<OptionParam> & params,int32_t index)255 RefPtr<FrameNode> OptionView::CreateMenuOption(bool optionsHasIcon, std::vector<OptionParam>& params, int32_t index)
256 {
257     auto option = Create(index);
258     CHECK_NULL_RETURN(option, nullptr);
259     auto row = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
260         AceType::MakeRefPtr<LinearLayoutPattern>(false));
261 
262 #ifdef OHOS_PLATFORM
263     constexpr char BUTTON_PASTE[] = "textoverlay.paste";
264     if (params[index].value == Localization::GetInstance()->GetEntryLetters(BUTTON_PASTE)) {
265         CreatePasteButton(optionsHasIcon, option, row, params[index].action);
266     } else {
267         CreateOption(optionsHasIcon, params, index, row, option);
268     }
269 #else
270     CreateOption(optionsHasIcon, params, index, row, option);
271 #endif
272     return option;
273 }
274 
CreateMenuOption(bool optionsHasIcon,const OptionValueInfo & value,const std::function<void ()> & onClickFunc,int32_t index,const std::string & icon)275 RefPtr<FrameNode> OptionView::CreateMenuOption(bool optionsHasIcon, const OptionValueInfo& value,
276     const std::function<void()>& onClickFunc, int32_t index, const std::string& icon)
277 {
278     auto option = Create(index);
279     CHECK_NULL_RETURN(option, nullptr);
280     auto row = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
281         AceType::MakeRefPtr<LinearLayoutPattern>(false));
282 
283 #ifdef OHOS_PLATFORM
284     constexpr char BUTTON_PASTE[] = "textoverlay.paste";
285     if (value.value == Localization::GetInstance()->GetEntryLetters(BUTTON_PASTE) || value.isPasteOption) {
286         CreatePasteButton(optionsHasIcon, option, row, onClickFunc, icon);
287     } else {
288         CreateOption(optionsHasIcon, value.value, icon, row, option, onClickFunc);
289     }
290 #else
291     CreateOption(optionsHasIcon, value.value, icon, row, option, onClickFunc);
292 #endif
293     return option;
294 }
295 
CreateSelectOption(const SelectParam & param,int32_t index)296 RefPtr<FrameNode> OptionView::CreateSelectOption(const SelectParam& param, int32_t index)
297 {
298     LOGI("create option value = %s", param.text.c_str());
299     auto option = Create(index);
300     auto row = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
301         AceType::MakeRefPtr<LinearLayoutPattern>(false));
302     row->MountToParent(option);
303 
304     auto pattern = option->GetPattern<OptionPattern>();
305     CHECK_NULL_RETURN(pattern, option);
306     // create icon node
307     RefPtr<FrameNode> iconNode;
308     if (param.symbolIcon != nullptr) {
309         iconNode = CreateSymbol(param.symbolIcon, row);
310     } else if (!param.icon.empty()) {
311         iconNode = CreateIcon(param.icon, row);
312         pattern->SetIcon(param.icon);
313     }
314     pattern->SetIconNode(iconNode);
315 
316     auto text = CreateText(param.text, row);
317     pattern->SetTextNode(text);
318     return option;
319 }
320 
321 } // namespace OHOS::Ace::NG
322