1 /*
2  * Copyright (c) 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/menu/menu_item_group/menu_item_group_pattern.h"
17 
18 #include <queue>
19 
20 #include "core/components_ng/base/ui_node.h"
21 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
22 #include "core/components_ng/pattern/menu/menu_layout_property.h"
23 #include "core/components_ng/pattern/menu/menu_pattern.h"
24 #include "core/components_ng/pattern/text/text_layout_property.h"
25 #include "core/components_v2/inspector/inspector_constants.h"
26 
27 namespace OHOS::Ace::NG {
OnMountToParentDone()28 void MenuItemGroupPattern::OnMountToParentDone()
29 {
30     ModifyFontSize();
31     ModifyDivider();
32     auto host = GetHost();
33     CHECK_NULL_VOID(host);
34     bool needDivider = false;
35     const auto& children = host->GetChildren();
36     for (const auto& child : children) {
37         if (child->GetTag() == V2::MENU_ITEM_ETS_TAG) {
38             auto itemNode = AceType::DynamicCast<FrameNode>(child);
39             CHECK_NULL_VOID(itemNode);
40             auto itemPattern = itemNode->GetPattern<MenuItemPattern>();
41             CHECK_NULL_VOID(itemPattern);
42             itemPattern->UpdateTextNodes();
43             itemPattern->UpdateNeedDivider(needDivider);
44             needDivider = true;
45         }
46     }
47 }
48 
ModifyFontSize()49 void MenuItemGroupPattern::ModifyFontSize()
50 {
51     auto menu = GetMenu();
52     CHECK_NULL_VOID(menu);
53     auto menuProperty = menu->GetLayoutProperty<MenuLayoutProperty>();
54     CHECK_NULL_VOID(menuProperty);
55     auto menuFontSize = menuProperty->GetFontSize();
56     if (!menuFontSize.has_value()) {
57         return;
58     }
59 
60     if (headerContent_) {
61         auto headerProperty = headerContent_->GetLayoutProperty<TextLayoutProperty>();
62         CHECK_NULL_VOID(headerProperty);
63         headerProperty->UpdateFontSize(menuFontSize.value());
64         headerContent_->MarkModifyDone();
65     }
66 
67     if (footerContent_) {
68         auto footerProperty = footerContent_->GetLayoutProperty<TextLayoutProperty>();
69         CHECK_NULL_VOID(footerProperty);
70         footerProperty->UpdateFontSize(menuFontSize.value());
71         footerContent_->MarkModifyDone();
72     }
73 }
74 
AddHeader(const RefPtr<NG::UINode> & header)75 void MenuItemGroupPattern::AddHeader(const RefPtr<NG::UINode>& header)
76 {
77     auto host = GetHost();
78     CHECK_NULL_VOID(host);
79     if (headerIndex_ < 0) {
80         headerIndex_ = itemStartIndex_;
81         host->AddChild(header);
82         itemStartIndex_++;
83     } else {
84         host->ReplaceChild(host->GetChildAtIndex(headerIndex_), header);
85     }
86     auto frameNode = AceType::DynamicCast<FrameNode>(header);
87     CHECK_NULL_VOID(frameNode);
88     if (headerContent_) {
89         headerContent_->MarkModifyDone();
90         headerContent_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
91     }
92 }
93 
AddFooter(const RefPtr<NG::UINode> & footer)94 void MenuItemGroupPattern::AddFooter(const RefPtr<NG::UINode>& footer)
95 {
96     auto host = GetHost();
97     CHECK_NULL_VOID(host);
98     if (footerIndex_ < 0) {
99         footerIndex_ = itemStartIndex_;
100         host->AddChild(footer);
101         itemStartIndex_++;
102     } else {
103         host->ReplaceChild(host->GetChildAtIndex(footerIndex_), footer);
104     }
105     auto frameNode = AceType::DynamicCast<FrameNode>(footer);
106     CHECK_NULL_VOID(frameNode);
107     if (footerContent_) {
108         footerContent_->MarkModifyDone();
109         footerContent_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
110     }
111 }
112 
GetMenu()113 RefPtr<FrameNode> MenuItemGroupPattern::GetMenu()
114 {
115     auto host = GetHost();
116     CHECK_NULL_RETURN(host, nullptr);
117     auto parent = host->GetParent();
118     while (parent) {
119         if (parent->GetTag() == V2::MENU_ETS_TAG) {
120             return DynamicCast<FrameNode>(parent);
121         }
122         parent = parent->GetParent();
123     }
124     return nullptr;
125 }
126 
GetHeaderContent()127 std::string MenuItemGroupPattern::GetHeaderContent()
128 {
129     CHECK_NULL_RETURN(headerContent_, "");
130     auto content = headerContent_->GetLayoutProperty<TextLayoutProperty>();
131     CHECK_NULL_RETURN(content, "");
132     return content->GetContentValue("");
133 }
134 
UpdateMenuItemIconInfo()135 void MenuItemGroupPattern::UpdateMenuItemIconInfo()
136 {
137     auto host = GetHost();
138     CHECK_NULL_VOID(host);
139     std::queue<RefPtr<UINode>> nodes;
140     nodes.emplace(host);
141     while (!nodes.empty()) {
142         auto currentNode = nodes.front();
143         nodes.pop();
144         if (DynamicCast<FrameNode>(currentNode) && DynamicCast<FrameNode>(currentNode)->GetPattern<MenuItemPattern>()) {
145             auto itemPattern = DynamicCast<FrameNode>(currentNode)->GetPattern<MenuItemPattern>();
146             hasSelectIcon_ |= itemPattern->HasSelectIcon();
147             hasStartIcon_ |= itemPattern->HasStartIcon();
148         }
149         for (const auto& child : currentNode->GetChildren()) {
150             nodes.emplace(child);
151         }
152     }
153 }
154 
ModifyDivider()155 void MenuItemGroupPattern::ModifyDivider()
156 {
157     auto menu = GetMenu();
158     CHECK_NULL_VOID(menu);
159     auto menuProperty = menu->GetLayoutProperty<MenuLayoutProperty>();
160     CHECK_NULL_VOID(menuProperty);
161 
162     auto divider = menuProperty->GetItemGroupDivider();
163     if (divider.has_value()) {
164         auto host = GetHost();
165         CHECK_NULL_VOID(host);
166         auto paintProperty = host->GetPaintProperty<MenuItemGroupPaintProperty>();
167         CHECK_NULL_VOID(paintProperty);
168         paintProperty->UpdateStrokeWidth(divider->strokeWidth);
169         paintProperty->UpdateStartMargin(divider->startMargin);
170         paintProperty->UpdateEndMargin(divider->endMargin);
171         paintProperty->UpdateDividerColor(divider->color);
172         paintProperty->UpdateNeedHeaderDivider(true);
173         paintProperty->UpdateNeedFooterDivider(true);
174     }
175 }
176 
OnExtItemPressed(bool press,bool beforeGroup)177 void MenuItemGroupPattern::OnExtItemPressed(bool press, bool beforeGroup)
178 {
179     auto host = GetHost();
180     CHECK_NULL_VOID(host);
181     auto paintProperty = host->GetPaintProperty<MenuItemGroupPaintProperty>();
182     CHECK_NULL_VOID(paintProperty);
183     if (beforeGroup) {
184         paintProperty->UpdateNeedHeaderDivider(!press);
185     } else {
186         paintProperty->UpdateNeedFooterDivider(!press);
187     }
188     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
189 }
190 
OnIntItemPressed(int32_t index,bool press)191 void MenuItemGroupPattern::OnIntItemPressed(int32_t index, bool press)
192 {
193     auto host = GetHost();
194     CHECK_NULL_VOID(host);
195     const size_t size = host->GetChildren().size();
196     auto parent = host->GetParent();
197     CHECK_NULL_VOID(parent);
198     auto currentIndex = parent->GetChildIndex(host);
199     if (index == itemStartIndex_ && headerContent_ == nullptr) {
200         OnExtItemPressed(press, true); // beforeGroup=true just to hide header divider
201         auto prevNode = parent->GetChildAtIndex(currentIndex - 1);
202         if (prevNode != nullptr && prevNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
203             auto pattern = DynamicCast<FrameNode>(prevNode)->GetPattern<MenuItemGroupPattern>();
204             CHECK_NULL_VOID(pattern);
205             pattern->OnExtItemPressed(press, false); // hide common divider for 2 group if another group before
206         }
207     }
208     if (size > 0 && index == static_cast<int32_t>(size - 1) && footerContent_ == nullptr) {
209         OnExtItemPressed(press, false); // beforeGroup=false just to hide footer divider
210         auto nextNode = parent->GetChildAtIndex(currentIndex + 1);
211         if (nextNode != nullptr && nextNode->GetTag() == V2::MENU_ITEM_GROUP_ETS_TAG) {
212             auto pattern = DynamicCast<FrameNode>(nextNode)->GetPattern<MenuItemGroupPattern>();
213             CHECK_NULL_VOID(pattern);
214             pattern->OnExtItemPressed(press, true); // hide common divider for 2 group if another group after
215         }
216     }
217 }
218 } // namespace OHOS::Ace::NG
219