Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "MaterialMenu.h"
24 : #include "MaterialMenuButton.h"
25 : #include "MaterialMenuSeparator.h"
26 :
27 : namespace STAPPLER_VERSIONIZED stappler::xenolith::material2d {
28 :
29 40 : Menu::~Menu() { }
30 :
31 20 : bool Menu::init() {
32 20 : if (!Surface::init(SurfaceStyle(
33 0 : ShapeFamily::RoundedCorners,
34 0 : ShapeStyle::ExtraSmall,
35 0 : Elevation::Level2,
36 40 : NodeStyle::SurfaceTonalElevated))) {
37 0 : return false;
38 : }
39 :
40 20 : _menuListener = addComponent(Rc<DataListener<MenuSource>>::create([this] (SubscriptionFlags flags) {
41 10 : handleSourceDirty();
42 10 : }));
43 :
44 20 : _scroll = addChild(Rc<ScrollView>::create(ScrollView::Vertical), ZOrder(1));
45 20 : _scroll->setAnchorPoint(Vec2(0, 1));
46 20 : _scroll->enableScissor(Padding(-2.0f));
47 :
48 20 : _controller = _scroll->setController(Rc<ScrollController>::create());
49 :
50 20 : return true;
51 : }
52 :
53 10 : bool Menu::init(MenuSource *source) {
54 10 : if (!init()) {
55 0 : return false;
56 : }
57 :
58 10 : setMenuSource(source);
59 :
60 10 : return true;
61 : }
62 :
63 10 : void Menu::setMenuSource(MenuSource *source) {
64 10 : _menuListener->setSubscription(source);
65 10 : }
66 :
67 10 : MenuSource *Menu::getMenuSource() const {
68 10 : return _menuListener->getSubscription();
69 : }
70 :
71 20 : void Menu::setEnabled(bool value) {
72 20 : _scroll->setEnabled(value);
73 20 : auto nodes = _controller->getNodes();
74 20 : if (!nodes.empty()) {
75 0 : for (auto it : nodes) {
76 0 : if (auto button = dynamic_cast<MenuButton *>(it.get())) {
77 0 : button->setEnabled(value);
78 : }
79 0 : }
80 : }
81 20 : }
82 :
83 0 : bool Menu::isEnabled() const {
84 0 : return _scroll->isEnabled();
85 : }
86 :
87 10 : void Menu::rebuildMenu() {
88 10 : _controller->clear();
89 10 : if (!_menuListener->getSubscription()) {
90 0 : return;
91 : }
92 :
93 10 : _controller->addPlaceholder(MenuLeadingHeight);
94 :
95 10 : auto &menuItems = _menuListener->getSubscription()->getItems();
96 60 : for (auto &item : menuItems) {
97 50 : if (item->getType() == MenuSourceItem::Type::Separator) {
98 10 : _controller->addItem([this, item] (const ScrollController::Item &) -> Rc<Node> {
99 20 : return createSeparator(this, item);
100 : }, MenuVerticalPadding);
101 40 : } else if (item->getType() == MenuSourceItem::Type::Button) {
102 40 : _controller->addItem([this, item] (const ScrollController::Item &) -> Rc<Node> {
103 80 : return createButton(this, static_cast<MenuSourceButton *>(item.get()));
104 : }, MenuItemHeight);
105 0 : } else if (item->getType() == MenuSourceItem::Type::Custom) {
106 0 : auto customItem = static_cast<MenuSourceCustom *>(item.get());
107 0 : const auto &func = customItem->getFactoryFunction();
108 0 : if (func) {
109 0 : float height = customItem->getHeight(this, _contentSize.width);
110 0 : _controller->addItem([this, func, item, customItem] (const ScrollController::Item &) -> Rc<Node> {
111 0 : return func(this, customItem);
112 : }, height);
113 : }
114 : }
115 : }
116 10 : _controller->addPlaceholder(MenuTrailingHeight);
117 10 : _scroll->setScrollDirty(true);
118 : }
119 :
120 40 : Rc<MenuButton> Menu::createButton(Menu *m, MenuSourceButton *btn) {
121 40 : return Rc<MenuButton>::create(m, btn);
122 : }
123 :
124 10 : Rc<MenuSeparator> Menu::createSeparator(Menu *m, MenuSourceItem *item) {
125 10 : return Rc<MenuSeparator>::create(m, item);
126 : }
127 :
128 10 : void Menu::onContentSizeDirty() {
129 10 : Surface::onContentSizeDirty();
130 10 : layoutSubviews();
131 10 : }
132 :
133 10 : void Menu::layoutSubviews() {
134 10 : auto &size = getContentSize();
135 10 : _scroll->setPosition(Vec2(0, size.height));
136 10 : _scroll->setContentSize(Size2(size.width, size.height));
137 10 : }
138 :
139 10 : void Menu::setMenuButtonCallback(const ButtonCallback &cb) {
140 10 : _callback = cb;
141 10 : }
142 :
143 0 : const Menu::ButtonCallback & Menu::getMenuButtonCallback() {
144 0 : return _callback;
145 : }
146 :
147 10 : void Menu::onMenuButtonPressed(MenuButton *button) {
148 10 : if (_callback) {
149 0 : _callback(button);
150 : }
151 10 : }
152 :
153 10 : ScrollView *Menu::getScroll() const {
154 10 : return _scroll;
155 : }
156 :
157 10 : void Menu::handleSourceDirty() {
158 10 : _controller->clear();
159 10 : rebuildMenu();
160 10 : }
161 :
162 : }
|