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 "MaterialFloatingMenu.h"
24 : #include "MaterialOverlayLayout.h"
25 : #include "MaterialMenuButton.h"
26 : #include "XL2dSceneLayout.h"
27 : #include "XLApplication.h"
28 : #include "XLFontController.h"
29 : #include "XL2dLayer.h"
30 : #include "XLInputListener.h"
31 :
32 : namespace STAPPLER_VERSIONIZED stappler::xenolith::material2d {
33 :
34 : class FloatingMenuLayout : public OverlayLayout {
35 : public:
36 0 : virtual ~FloatingMenuLayout() { }
37 :
38 : virtual bool init(MenuSource *source, const Vec2 &globalOrigin, FloatingMenu::Binding b, Menu *root);;
39 :
40 : virtual void onPushTransitionEnded(SceneContent2d *l, bool replace) override;
41 :
42 : protected:
43 : FloatingMenu *_menu = nullptr;
44 : };
45 :
46 0 : bool FloatingMenuLayout::init(MenuSource *source, const Vec2 &globalOrigin, FloatingMenu::Binding b, Menu *root) {
47 0 : auto menu = Rc<FloatingMenu>::create(source, root);
48 :
49 0 : if (!OverlayLayout::init(globalOrigin, b, menu, Size2())) {
50 0 : return false;
51 : }
52 :
53 0 : _menu = menu;
54 :
55 0 : _readyCallback = [this] (bool ready) {
56 0 : _menu->setReady(ready);
57 0 : };
58 :
59 0 : _closeCallback = [this] () {
60 0 : if (auto &cb = _menu->getCloseCallback()) {
61 0 : cb();
62 : }
63 0 : };
64 :
65 0 : return true;
66 0 : }
67 :
68 0 : void FloatingMenuLayout::onPushTransitionEnded(SceneContent2d *l, bool replace) {
69 0 : float w = _menu->getMenuWidth(this);
70 0 : float h = _menu->getMenuHeight(this, w);
71 :
72 0 : _fullSize = Size2(w, h);
73 :
74 0 : OverlayLayout::onPushTransitionEnded(l, replace);
75 0 : }
76 :
77 0 : void FloatingMenu::push(SceneContent2d *content, MenuSource *source, const Vec2 &globalOrigin, Binding b, Menu *root) {
78 0 : auto l = Rc<FloatingMenuLayout>::create(source, globalOrigin, b, root);
79 :
80 0 : content->pushOverlay(l);
81 0 : }
82 :
83 0 : bool FloatingMenu::init(MenuSource *source, Menu *root) {
84 0 : if (!Menu::init(source)) {
85 0 : return false;
86 : }
87 :
88 0 : setMenuButtonCallback(std::bind(&FloatingMenu::onMenuButton, this, std::placeholders::_1));
89 :
90 0 : _root = root;
91 :
92 0 : if (_root) {
93 0 : setElevation(Elevation(toInt(_root->getStyleOrigin().elevation) + 1));
94 : } else {
95 0 : setElevation(Elevation::Level3);
96 : }
97 :
98 0 : _scroll->setIndicatorVisible(_ready);
99 :
100 0 : return true;
101 : }
102 :
103 0 : void FloatingMenu::setCloseCallback(const CloseCallback &cb) {
104 0 : _closeCallback = cb;
105 0 : }
106 0 : const FloatingMenu::CloseCallback & FloatingMenu::getCloseCallback() const {
107 0 : return _closeCallback;
108 : }
109 :
110 0 : void FloatingMenu::close() {
111 0 : if (!_running) {
112 0 : return;
113 : }
114 :
115 0 : stopAllActions();
116 0 : if (auto l = dynamic_cast<FloatingMenuLayout *>(_parent)) {
117 0 : if (auto c = l->getSceneContent()) {
118 0 : c->popOverlay(l);
119 : }
120 : }
121 : }
122 :
123 0 : void FloatingMenu::closeRecursive() {
124 0 : if (_root) {
125 0 : if (auto r = dynamic_cast<FloatingMenu *>(_root)) {
126 0 : r->close();
127 : }
128 : }
129 0 : close();
130 0 : }
131 :
132 0 : void FloatingMenu::onCapturedTap() {
133 0 : close();
134 0 : }
135 :
136 0 : float FloatingMenu::getMenuWidth(Node *root) {
137 0 : float minWidth = 0;
138 0 : auto &items = _menuListener->getSubscription()->getItems();
139 0 : for (auto &item : items) {
140 0 : if (item->getType() == MenuSourceItem::Type::Custom) {
141 0 : float w = static_cast<MenuSourceCustom *>(item.get())->getMinWidth();
142 0 : if (w > minWidth) {
143 0 : minWidth = w;
144 : }
145 0 : } else if (item->getType() == MenuSourceItem::Type::Button) {
146 0 : auto btn = static_cast<MenuSourceButton *>(item.get());
147 0 : auto c = Application::getInstance()->getExtension<font::FontController>();
148 :
149 0 : float w = MenuButton::getMaxWidthForButton(btn, c, root->getInputDensity());
150 0 : if (w > minWidth) {
151 0 : minWidth = w;
152 : }
153 : }
154 : }
155 :
156 0 : const float incr = MenuHorizontalIncrement;
157 0 : const auto &size = root->getContentSize();
158 :
159 0 : minWidth = incr * ceilf(minWidth / incr);
160 0 : if (minWidth > size.width - incr / 2) {
161 0 : minWidth = size.width - incr / 2;
162 : }
163 :
164 0 : return minWidth;
165 : }
166 :
167 0 : float FloatingMenu::getMenuHeight(Node *root, float width) {
168 0 : float height = MenuLeadingHeight + MenuTrailingHeight;
169 0 : auto &items = _menuListener->getSubscription()->getItems();
170 0 : for (auto &item : items) {
171 0 : if (item->getType() == MenuSourceItem::Type::Custom) {
172 0 : height += static_cast<MenuSourceCustom *>(item.get())->getHeight(this, width);
173 0 : } else if (item->getType() == MenuSourceItem::Type::Button) {
174 0 : height += MenuItemHeight;
175 : } else {
176 0 : height += MenuVerticalPadding;
177 : }
178 : }
179 :
180 0 : const float incr = MenuHorizontalIncrement;
181 0 : const auto &size = root->getContentSize();
182 :
183 0 : if (height > size.height - incr / 2) {
184 0 : height = size.height - incr / 2;
185 : }
186 :
187 0 : return height;
188 : }
189 :
190 0 : void FloatingMenu::setReady(bool value) {
191 0 : if (value != _ready) {
192 0 : _ready = value;
193 0 : _scroll->setIndicatorVisible(_ready);
194 : }
195 0 : }
196 :
197 0 : bool FloatingMenu::isReady() const {
198 0 : return _ready;
199 : }
200 :
201 0 : void FloatingMenu::onMenuButton(MenuButton *btn) {
202 0 : if (!btn->getMenuSourceButton()->getNextMenu()) {
203 0 : setEnabled(false);
204 0 : runAction(Rc<Sequence>::create(0.15f, std::bind(&FloatingMenu::closeRecursive, this)));
205 : }
206 0 : }
207 :
208 : }
|