1 /*
2  * Copyright (c) 2021-2022 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/navigation_bar/navigation_bar_component_v2.h"
17 
18 #include "core/components/display/display_component.h"
19 #include "core/components/flex/flex_item_component.h"
20 #include "core/components/navigation_bar/navigation_bar_element.h"
21 #include "core/components/padding/padding_component.h"
22 #include "core/components/transform/transform_component.h"
23 
24 namespace OHOS::Ace {
25 namespace {
26 
27 constexpr double MIDDLE_ZONE_FLEX_GROW = 1.0;
28 constexpr double MIDDLE_ZONE_FLEX_SHRINK = 1.0;
29 constexpr double MENU_DEFAULT_HEIGHT = 56.0;
30 
31 } // namespace
32 
BuildMiniLayer()33 RefPtr<Component> NavigationBarBuilder::BuildMiniLayer()
34 {
35     Edge padding;
36     RefPtr<RowComponent> container =
37         AceType::MakeRefPtr<RowComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, std::list<RefPtr<Component>>());
38     container->SetTextDirection(direction_);
39 
40     if (!declaration_->hideBarBackButton) {
41         IconImage backIcon(theme_->GetBackResourceId(), menuIconSize_, menuIconSize_);
42         backIcon.image->SetTextDirection(direction_);
43         backIcon.image->SetMatchTextDirection(true);
44         backIcon.image->SetColor(theme_->GetMenuIconColor());
45         auto backButton = BuildIconButton(theme_, menuZoneSize_, menuZoneSize_, backIcon, backClickMarker_);
46         EventMarker clickEventId([weakContext = context_](const BaseEventInfo* info) {
47             auto context = weakContext.Upgrade();
48             if (!context) {
49                 LOGW("context is empty");
50                 return;
51             }
52             if (!context->CallRouterBackToPopPage()) {
53                 context->Finish(false);
54             }
55         });
56         backButton->SetClickedEventId(clickEventId);
57         container->AppendChild(GenerateAccessibilityComposed(StringUtils::StringToInt(id_), "backButton", backButton));
58         container->AppendChild(BuildPadding(theme_->GetTitleMinPadding().Value()));
59         padding.SetLeft(theme_->GetDefaultPaddingStart());
60     } else {
61         container->AppendChild(BuildPadding(theme_->GetMaxPaddingStart().Value()));
62     }
63 
64     container->AppendChild(BuildTitle());
65     container->SetUpdateType(UpdateType::REBUILD);
66 
67     if (AddMenu(container)) {
68         padding.SetRight(theme_->GetDefaultPaddingEnd());
69     } else {
70         padding.SetRight(theme_->GetMaxPaddingStart() - theme_->GetTitleMinPadding());
71     }
72 
73     return BuildAnimationContainer(container, theme_->GetHeight(), padding);
74 }
75 
BuildFullLayer()76 RefPtr<Component> NavigationBarBuilder::BuildFullLayer()
77 {
78     RefPtr<ColumnComponent> columnContainer =
79         AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::STRETCH, std::list<RefPtr<Component>>());
80     columnContainer->SetTextDirection(direction_);
81     columnContainer->SetCrossAxisSize(CrossAxisSize::MAX);
82     columnContainer->AppendChild(BuildTitle());
83 
84     RefPtr<RowComponent> menuContainer =
85         AceType::MakeRefPtr<RowComponent>(FlexAlign::FLEX_END, FlexAlign::CENTER, std::list<RefPtr<Component>>());
86     menuContainer->SetTextDirection(direction_);
87     AddMenu(menuContainer);
88     auto menusBox = AceType::MakeRefPtr<BoxComponent>();
89     menusBox->SetHeight(theme_->GetHeight().Value(), theme_->GetHeight().Unit());
90     menusBox->SetChild(menuContainer);
91 
92     RefPtr<ColumnComponent> container =
93         AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::STRETCH, std::list<RefPtr<Component>>());
94     container->SetTextDirection(direction_);
95     container->SetCrossAxisSize(CrossAxisSize::MAX);
96     container->AppendChild(menusBox);
97     container->AppendChild(columnContainer);
98 
99     Edge padding;
100     padding.SetLeft(theme_->GetMaxPaddingStart());
101     padding.SetRight(theme_->GetDefaultPaddingEnd());
102     return BuildAnimationContainer(container, theme_->GetHeightEmphasize(), padding);
103 }
104 
BuildFreeModeBar()105 RefPtr<Component> NavigationBarBuilder::BuildFreeModeBar()
106 {
107     Edge padding;
108     padding.SetLeft(theme_->GetMaxPaddingStart());
109     padding.SetRight(theme_->GetMaxPaddingStart());
110     RefPtr<ColumnComponent> columnContainer =
111         AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::STRETCH, std::list<RefPtr<Component>>());
112     columnContainer->SetTextDirection(direction_);
113     columnContainer->SetCrossAxisSize(CrossAxisSize::MAX);
114     columnContainer->AppendChild(BuildTitle());
115     auto columnBox = AceType::MakeRefPtr<BoxComponent>();
116     columnBox->SetChild(columnContainer);
117     columnBox->SetPadding(padding);
118 
119     padding.SetLeft(theme_->GetDefaultPaddingEnd());
120     padding.SetRight(theme_->GetDefaultPaddingEnd());
121     RefPtr<RowComponent> menuContainer =
122         AceType::MakeRefPtr<RowComponent>(FlexAlign::FLEX_END, FlexAlign::CENTER, std::list<RefPtr<Component>>());
123     menuContainer->SetTextDirection(direction_);
124     AddMenu(menuContainer);
125     auto menusBox = AceType::MakeRefPtr<BoxComponent>();
126     menusBox->SetHeight(theme_->GetHeight().Value(), theme_->GetHeight().Unit());
127     menusBox->SetChild(menuContainer);
128     menusBox->SetPadding(padding);
129 
130     auto height = !declaration_->subTitle.empty() ? Dimension(134, DimensionUnit::VP) : theme_->GetHeightEmphasize();
131     RefPtr<CollapsingNavigationBarComponent> collapsingNavigationBar =
132         AceType::MakeRefPtr<CollapsingNavigationBarComponent>(
133             titleComposed_, subTitleComposed_, declaration_->titleModeChangedEvent);
134     collapsingNavigationBar->AppendChild(menusBox);
135     collapsingNavigationBar->AppendChild(columnBox);
136     collapsingNavigationBar->SetMinHeight(theme_->GetHeight());
137 
138     return BuildAnimationContainer(collapsingNavigationBar, height, Edge());
139 }
140 
BuildAnimationContainer(const RefPtr<Component> & content,const Dimension & height,const Edge & padding)141 RefPtr<Component> NavigationBarBuilder::BuildAnimationContainer(
142     const RefPtr<Component>& content, const Dimension& height, const Edge& padding)
143 {
144     auto boxContainer = AceType::MakeRefPtr<BoxComponent>();
145     boxContainer->SetChild(content);
146     boxContainer->SetPadding(padding);
147     auto display = AceType::MakeRefPtr<DisplayComponent>();
148     display->SetChild(boxContainer);
149     if (!declaration_->hideBar) {
150         boxContainer->SetHeight(height, declaration_->animationOption);
151         display->SetOpacity(1.0, declaration_->animationOption);
152     } else {
153         boxContainer->SetHeight(Dimension(0.0, height.Unit()), declaration_->animationOption);
154         display->SetOpacity(0.0, declaration_->animationOption);
155     }
156     return display;
157 }
158 
BuildTitle()159 RefPtr<Component> NavigationBarBuilder::BuildTitle()
160 {
161     RefPtr<ColumnComponent> titleContainer = AceType::MakeRefPtr<ColumnComponent>(
162         FlexAlign::FLEX_START, FlexAlign::FLEX_START, std::list<RefPtr<Component>>());
163     titleContainer->SetTextDirection(direction_);
164     titleContainer->SetMainAxisSize(MainAxisSize::MIN);
165 
166     if (declaration_->customTitle || !declaration_->title.empty()) {
167         RefPtr<Component> title;
168         if (declaration_->customTitle) {
169             title = declaration_->customTitle;
170         } else {
171             title = BuildTitleText(
172                 declaration_->title, theme_->GetTitleColor(), theme_->GetTitleFontSize(), FontWeight::W500);
173         }
174         auto transform = AceType::MakeRefPtr<TransformComponent>();
175         transform->SetChild(title);
176         transform->SetOriginDimension(DimensionOffset(Offset(0.0, 0.0)));
177         titleComposed_ = GenerateAccessibilityComposed(StringUtils::StringToInt(id_), "titleText", transform);
178         titleContainer->AppendChild(titleComposed_);
179     }
180     if (!declaration_->subTitle.empty()) {
181         titleContainer->AppendChild(BuildPadding(2, true));
182         auto subTitleText = BuildTitleText(
183             declaration_->subTitle, theme_->GetSubTitleColor(), theme_->GetSubTitleFontSize(), FontWeight::W400);
184         auto display = AceType::MakeRefPtr<DisplayComponent>(subTitleText);
185         subTitleComposed_ = GenerateAccessibilityComposed(StringUtils::StringToInt(id_), "subTitleText", display);
186         titleContainer->AppendChild(subTitleComposed_);
187     }
188 
189     return AceType::MakeRefPtr<FlexItemComponent>(MIDDLE_ZONE_FLEX_GROW, MIDDLE_ZONE_FLEX_SHRINK, 0.0, titleContainer);
190 }
191 
AddMenu(const RefPtr<ComponentGroup> & container)192 bool NavigationBarBuilder::AddMenu(const RefPtr<ComponentGroup>& container)
193 {
194     if (declaration_->customMenus) {
195         LOGE("arg is customMenus.");
196         container->AppendChild(declaration_->customMenus);
197         return true;
198     }
199     uint32_t showInBarSize = 0;
200     bool hasRoom = true;
201     uint32_t mostShowInBarSize =
202         menuCount_ > 0 ? static_cast<uint32_t>(menuCount_) : theme_->GetMostMenuItemCountInBar();
203     bool needAddPadding = false;
204     menu_ = AceType::MakeRefPtr<MenuComponent>("", "navigationMenu");
205     auto ctx = context_.Upgrade();
206     if (ctx) {
207         auto themeManager = ctx->GetThemeManager();
208         if (themeManager) {
209             menu_->SetTheme(themeManager->GetTheme<SelectTheme>());
210         }
211     }
212     auto menusSize = declaration_->menuItems.size();
213     for (const auto& menuItem : declaration_->menuItems) {
214         hasRoom = hasRoom && ((showInBarSize < mostShowInBarSize - 1) ||
215             (showInBarSize == mostShowInBarSize - 1 && menusSize == mostShowInBarSize) ||
216             (showInBarSize < mostShowInBarSize && SystemProperties::GetDeviceType() == DeviceType::TV));
217         if (hasRoom) {
218             showInBarSize++;
219             IconImage menuIcon(menuItem->icon, menuIconSize_, menuIconSize_);
220             auto optionButton = BuildIconButton(theme_, menuZoneSize_, menuZoneSize_, menuIcon);
221             if (theme_->GetMenuItemPadding().Value() > 0.0 && needAddPadding) {
222                 container->AppendChild(BuildPadding(theme_->GetMenuItemPadding().Value()));
223             }
224             optionButton->SetClickedEventId(menuItem->actionWithParam);
225             container->AppendChild(
226                 GenerateAccessibilityComposed(StringUtils::StringToInt(id_), "optionButton", optionButton));
227             needAddPadding = true;
228         } else {
229             AddOption(menuItem);
230         }
231     }
232     if (menu_->GetOptionCount() > 0) {
233         // add collapse menu
234         IconImage moreIcon(theme_->GetMoreResourceId(), menuIconSize_, menuIconSize_);
235         moreIcon.image->SetColor(theme_->GetMenuIconColor());
236         moreButton_ = BuildIconButton(theme_, menuZoneSize_, menuZoneSize_, moreIcon);
237         container->AppendChild(GenerateAccessibilityComposed(StringUtils::StringToInt(id_), "moreButton", moreButton_));
238         container->AppendChild(menu_);
239     }
240     BindMoreButtonClickEvent();
241     return showInBarSize > 0 || menu_->GetOptionCount() > 0;
242 }
243 
AddOption(const RefPtr<ToolBarItem> & menuItem)244 void NavigationBarBuilder::AddOption(const RefPtr<ToolBarItem>& menuItem)
245 {
246     auto optionComponent = AceType::MakeRefPtr<OptionComponent>();
247     auto ctx = context_.Upgrade();
248     if (ctx) {
249         auto themeManager = ctx->GetThemeManager();
250         if (themeManager) {
251             optionComponent->InitTheme(themeManager);
252         }
253     }
254     optionComponent->SetText(AceType::MakeRefPtr<TextComponent>(menuItem->value));
255     optionComponent->SetValue(menuItem->value);
256     optionComponent->SetIcon(AceType::MakeRefPtr<ImageComponent>(menuItem->icon));
257     optionComponent->SetClickEvent(menuItem->action);
258     menu_->AppendOption(optionComponent);
259 }
260 
BindMoreButtonClickEvent()261 void NavigationBarBuilder::BindMoreButtonClickEvent()
262 {
263     if (!moreButton_) {
264         return;
265     }
266     EventMarker clickEventId([menu = menu_, context = context_](const BaseEventInfo* info) {
267         if (!menu) {
268             return;
269         }
270         auto showMenuFunc = menu->GetTargetCallback();
271         if (!showMenuFunc) {
272             return;
273         }
274         auto ctx = context.Upgrade();
275         if (!ctx) {
276             return;
277         }
278         double morePopupOffsetX = 0.0;
279         morePopupOffsetX = ctx->GetStageRect().Width();
280         showMenuFunc("", Offset(morePopupOffsetX, MENU_DEFAULT_HEIGHT));
281     });
282     moreButton_->SetClickedEventId(clickEventId);
283 }
284 
285 } // namespace OHOS::Ace
286