Line data Source code
1 : /**
2 : Copyright (c) 2024 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 "MaterialMultiViewLayout.h"
24 : #include "XL2dScrollController.h"
25 : #include "XL2dActionAcceleratedMove.h"
26 : #include "XLInputListener.h"
27 : #include "XL2dScrollView.h"
28 : #include "XLAction.h"
29 : #include "XLActionEase.h"
30 :
31 : namespace STAPPLER_VERSIONIZED stappler::xenolith::material2d {
32 :
33 0 : bool MultiViewLayout::Generator::init() {
34 0 : if (!Component::init()) {
35 0 : return false;
36 : }
37 :
38 0 : return true;
39 : }
40 :
41 0 : bool MultiViewLayout::Generator::init(size_t count, const IndexViewCallback &gen) {
42 0 : if (!Component::init()) {
43 0 : return false;
44 : }
45 :
46 0 : _viewCount = count;
47 0 : _makeViewByIndex = gen;
48 :
49 0 : return true;
50 : }
51 0 : bool MultiViewLayout::Generator::init(const SequenceViewCallback &seq) {
52 0 : if (!Component::init()) {
53 0 : return false;
54 : }
55 :
56 0 : _makeViewSeq = seq;
57 :
58 0 : return true;
59 : }
60 :
61 0 : bool MultiViewLayout::Generator::isViewLocked(ScrollView *, int64_t index) {
62 0 : return !_enabled;
63 : }
64 :
65 0 : Rc<ScrollView> MultiViewLayout::Generator::makeIndexView(int64_t viewIndex) {
66 0 : if (_makeViewSeq) {
67 0 : return _makeViewSeq(nullptr, viewIndex, 0);
68 0 : } else if (viewIndex >= 0 && viewIndex < int64_t(_viewCount) && _makeViewByIndex) {
69 0 : return _makeViewByIndex(viewIndex);
70 : }
71 0 : return nullptr;
72 : }
73 :
74 0 : Rc<ScrollView> MultiViewLayout::Generator::makeNextView(ScrollView *view, int64_t viewIndex) {
75 0 : if (!_enabled) {
76 0 : return nullptr;
77 : }
78 0 : if (_makeViewSeq) {
79 0 : return _makeViewSeq(view, viewIndex, 1);
80 0 : } else if (_makeViewByIndex) {
81 0 : return _makeViewByIndex(viewIndex + 1);
82 : }
83 0 : return nullptr;
84 : }
85 :
86 0 : Rc<ScrollView> MultiViewLayout::Generator::makePrevView(ScrollView *view, int64_t viewIndex) {
87 0 : if (!_enabled) {
88 0 : return nullptr;
89 : }
90 0 : if (_makeViewSeq) {
91 0 : return _makeViewSeq(view, viewIndex, -1);
92 0 : } else if (_makeViewByIndex) {
93 0 : return _makeViewByIndex(viewIndex - 1);
94 : }
95 0 : return nullptr;
96 : }
97 :
98 0 : bool MultiViewLayout::Generator::isInfinite() const {
99 0 : return _viewCount == maxOf<size_t>();
100 : }
101 :
102 0 : int64_t MultiViewLayout::Generator::getViewCount() const {
103 0 : return _viewCount;
104 : }
105 :
106 0 : void MultiViewLayout::Generator::setViewSelectedCallback(const ViewCallback &cb) {
107 0 : _viewSelectedCallback = cb;
108 0 : }
109 :
110 0 : void MultiViewLayout::Generator::setViewCreatedCallback(const ViewCallback &cb) {
111 0 : _viewCreatedCallback = cb;
112 0 : }
113 :
114 0 : void MultiViewLayout::Generator::setApplyViewCallback(const ViewCallback &cb) {
115 0 : _applyViewCallback = cb;
116 0 : }
117 :
118 0 : void MultiViewLayout::Generator::setApplyProgressCallback(const ProgressCallback &cb) {
119 0 : _applyProgressCallback = cb;
120 0 : }
121 :
122 0 : void MultiViewLayout::Generator::onViewSelected(ScrollView *current, int64_t id) {
123 0 : if (_viewSelectedCallback) {
124 0 : _viewSelectedCallback(current, id);
125 : }
126 0 : }
127 0 : void MultiViewLayout::Generator::onViewCreated(ScrollView *current, int64_t id) {
128 0 : if (_viewCreatedCallback) {
129 0 : _viewCreatedCallback(current, id);
130 : }
131 0 : }
132 :
133 0 : void MultiViewLayout::Generator::onApplyView(ScrollView *current, int64_t id) {
134 0 : if (_applyViewCallback) {
135 0 : _applyViewCallback(current, id);
136 : }
137 0 : }
138 0 : void MultiViewLayout::Generator::onApplyProgress(ScrollView *current, ScrollView *n, float progress) {
139 0 : if (_applyProgressCallback) {
140 0 : _applyProgressCallback(current, n, progress);
141 : }
142 0 : }
143 :
144 0 : MultiViewLayout *MultiViewLayout::Generator::getLayout() {
145 0 : return dynamic_cast<MultiViewLayout *>(_owner);
146 : }
147 :
148 0 : bool MultiViewLayout::init(Generator *gen) {
149 0 : if (!FlexibleLayout::init()) {
150 0 : return false;
151 : }
152 :
153 0 : if (!gen) {
154 0 : _generator = addComponent(Rc<Generator>::create());
155 : } else {
156 0 : _generator = addComponent(Rc<Generator>(gen));
157 : }
158 :
159 0 : _swipeListener = addInputListener(Rc<InputListener>::create());
160 0 : _swipeListener->addSwipeRecognizer([this] (const GestureSwipe &s) -> bool {
161 0 : if (s.event == GestureEvent::Began) {
162 0 : if (beginSwipe(s.delta)) {
163 0 : _swipeListener->setExclusiveForTouch(s.getId());
164 0 : return true;
165 : }
166 0 : return false;
167 0 : } else if (s.event == GestureEvent::Activated) {
168 0 : return setSwipeProgress(s.delta / s.density);
169 : } else {
170 0 : return endSwipeProgress(s.delta / s.density, s.velocity / s.density);
171 : }
172 : return true;
173 : }, TapDistanceAllowed, true);
174 :
175 0 : return true;
176 : }
177 :
178 0 : void MultiViewLayout::onPush(SceneContent2d *l, bool replace) {
179 0 : FlexibleLayout::onPush(l, replace);
180 :
181 0 : if (!_currentView) {
182 0 : if (auto view = makeInitialView()) {
183 0 : _currentView = view;
184 0 : _nextView = nullptr;
185 :
186 0 : onViewSelected(_currentView, _currentViewIndex);
187 0 : setBaseNode(_currentView, ZOrder(1));
188 0 : applyView(_currentView);
189 0 : }
190 : }
191 0 : }
192 :
193 0 : void MultiViewLayout::setGenerator(Generator *gen) {
194 0 : if (_generator) {
195 0 : removeComponent(_generator);
196 : }
197 0 : addComponentItem(gen);
198 0 : _generator = gen;
199 0 : }
200 :
201 0 : MultiViewLayout::Generator *MultiViewLayout::getGenerator() const {
202 0 : return _generator;
203 : }
204 :
205 0 : int64_t MultiViewLayout::getCurrentIndex() const {
206 0 : return _currentViewIndex;
207 : }
208 :
209 0 : void MultiViewLayout::showNextView(float val) {
210 0 : setFlexibleLevelAnimated(1.0f, val);
211 0 : auto a = Rc<ActionProgress>::create(val, [this] (float time) {
212 0 : _swipeProgress = _contentSize.width * time * -1.0f;
213 0 : onSwipeProgress();
214 0 : }, [] () {
215 :
216 0 : }, [this] () {
217 0 : endSwipeProgress(Vec2(0.0f, 0.0f), Vec2(0.0f, 0.0f));
218 0 : });
219 0 : runAction(Rc<EaseQuadraticActionInOut>::create(a), "ListAction"_tag);
220 0 : }
221 :
222 0 : void MultiViewLayout::showPrevView(float val) {
223 0 : setFlexibleLevelAnimated(1.0f, val);
224 0 : auto a = Rc<ActionProgress>::create(val, [this] (float time) {
225 0 : _swipeProgress = _contentSize.width * time;
226 0 : onSwipeProgress();
227 0 : }, [] () {
228 :
229 0 : }, [this] () {
230 0 : endSwipeProgress(Vec2(0.0f, 0.0f), Vec2(0.0f, 0.0f));
231 0 : });
232 0 : runAction(Rc<EaseQuadraticActionInOut>::create(a), "ListAction"_tag);
233 0 : }
234 :
235 0 : void MultiViewLayout::showIndexView(int64_t idx, float val) {
236 0 : if (_currentViewIndex < idx) {
237 0 : _currentViewIndex = idx - 1;
238 0 : showNextView(val);
239 0 : } else if (_currentViewIndex > idx) {
240 0 : _currentViewIndex = idx + 1;
241 0 : showPrevView(val);
242 : }
243 0 : }
244 :
245 0 : Rc<ScrollView> MultiViewLayout::makeInitialView() {
246 0 : return _generator->makeIndexView(_currentViewIndex);
247 : }
248 :
249 0 : bool MultiViewLayout::onSwipeProgress() {
250 0 : if (!_currentView) {
251 0 : return false;
252 : }
253 :
254 0 : _currentView->setPositionX(_swipeProgress);
255 :
256 0 : if (_swipeProgress > 0.0f) {
257 0 : if (_nextView) {
258 0 : _nextView->removeFromParent();
259 0 : _nextView = nullptr;
260 : }
261 :
262 0 : if (!_prevView) {
263 : //log::format("MultiViewLayout", "prevView %d", _currentViewIndex - 1);
264 0 : if (auto view = _generator->makePrevView(_currentView, _currentViewIndex)) {
265 0 : onPrevViewCreated(_currentView, view);
266 0 : _prevView = addChild(view, _currentView->getLocalZOrder());
267 0 : }
268 : }
269 :
270 0 : if (!_prevView) {
271 0 : return false;
272 : }
273 :
274 0 : if (_prevView) {
275 0 : applyViewProgress(_currentView, _prevView, fabs(_swipeProgress) / _contentSize.width);
276 : }
277 0 : } else if (_swipeProgress < 0.0f) {
278 0 : if (_prevView) {
279 0 : _prevView->removeFromParent();
280 0 : _prevView = nullptr;
281 : }
282 :
283 0 : if (!_nextView) {
284 0 : if (auto view = _generator->makeNextView(_currentView, _currentViewIndex)) {
285 0 : onNextViewCreated(_currentView, view);
286 0 : _nextView = addChild(view, _currentView->getLocalZOrder());
287 0 : }
288 : }
289 :
290 0 : if (!_nextView) {
291 0 : return false;
292 : }
293 :
294 0 : if (_nextView) {
295 0 : applyViewProgress(_currentView, _nextView, fabs(_swipeProgress) / _contentSize.width);
296 : }
297 : } else {
298 0 : if (_nextView) {
299 0 : _nextView->removeFromParent();
300 0 : _nextView = nullptr;
301 : }
302 :
303 0 : if (_prevView) {
304 0 : _prevView->removeFromParent();
305 0 : _prevView = nullptr;
306 : }
307 :
308 0 : applyView(_currentView);
309 : }
310 0 : if (_nextView) {
311 0 : _nextView->setPositionX(_swipeProgress + _contentSize.width);
312 : }
313 0 : if (_prevView) {
314 0 : _prevView->setPositionX(_swipeProgress - _contentSize.width);
315 : }
316 0 : return true;
317 : }
318 :
319 0 : bool MultiViewLayout::beginSwipe(const Vec2 &diff) {
320 0 : if (_currentView) {
321 0 : if (_generator->isViewLocked(_currentView, _currentViewIndex)) {
322 0 : return false;
323 : }
324 : } else {
325 0 : return false;
326 : }
327 0 : if (_swipeProgress > _contentSize.width * 0.75f && _prevView) {
328 0 : setPrevView(_currentViewIndex - 1, _swipeProgress - _contentSize.width);
329 0 : } else if (_swipeProgress < -_contentSize.width * 0.75f && _nextView) {
330 0 : setNextView(_currentViewIndex + 1, _swipeProgress + _contentSize.width);
331 : }
332 0 : if (fabs(diff.x) < fabs(diff.y)) {
333 0 : return false;
334 : } else {
335 0 : setFlexibleLevelAnimated(1.0f, 0.25f);
336 : }
337 0 : return true;
338 : }
339 :
340 0 : bool MultiViewLayout::setSwipeProgress(const Vec2 &delta) {
341 0 : _currentView->stopActionByTag("ListAction"_tag);
342 0 : float diff = delta.x;
343 0 : if (!_generator->isInfinite()) {
344 0 : if (_currentViewIndex == 0 && _swipeProgress + diff > 0.0f) {
345 0 : _swipeProgress = 0.0f;
346 0 : onSwipeProgress();
347 0 : } else if (_currentViewIndex == _generator->getViewCount() - 1 && _swipeProgress + diff < 0.0f) {
348 0 : _swipeProgress = 0.0f;
349 0 : onSwipeProgress();
350 : } else {
351 0 : _swipeProgress += diff;
352 0 : onSwipeProgress();
353 : }
354 : } else {
355 0 : _swipeProgress += diff;
356 0 : if (!onSwipeProgress()) {
357 0 : _swipeProgress = 0.0f;
358 0 : onSwipeProgress();
359 0 : return false;
360 : }
361 : }
362 0 : return true;
363 : }
364 :
365 0 : bool MultiViewLayout::endSwipeProgress(const Vec2 &delta, const Vec2 &velocity) {
366 0 : if (!_currentView) {
367 0 : return false;
368 : }
369 : /*if (fabsf(_swipeProgress) < 20.0f) {
370 : _swipeProgress = 0.0f;
371 : onSwipeProgress();
372 : return false;
373 : }*/
374 :
375 0 : float v = fabsf(velocity.x);
376 0 : float a = 5000;
377 0 : float t = v / a;
378 0 : float dx = v * t - (a * t * t) / 2;
379 :
380 0 : float pos = _swipeProgress + ((velocity.x > 0) ? (dx) : (-dx));
381 :
382 0 : float yPos = _currentView->getPosition().y;
383 :
384 0 : auto from = Vec2(_swipeProgress, yPos);
385 :
386 0 : Rc<ActionInterval> action;
387 0 : if (pos > _contentSize.width / 2 && _prevView) {
388 0 : action = Rc<Sequence>::create(
389 0 : ActionAcceleratedMove::createBounce(5000, from, Vec2(_contentSize.width, yPos), Vec2(velocity.x, 0),
390 0 : 200000, std::bind(&MultiViewLayout::onSwipeAction, this, std::placeholders::_1)),
391 0 : [this] {
392 0 : setPrevView(_currentViewIndex - 1);
393 0 : });
394 0 : } else if (pos < -_contentSize.width / 2 && _nextView) {
395 0 : action = Rc<Sequence>::create(
396 0 : ActionAcceleratedMove::createBounce(5000, from, Vec2(-_contentSize.width, yPos), Vec2(velocity.x, 0),
397 0 : 200000, std::bind(&MultiViewLayout::onSwipeAction, this, std::placeholders::_1)),
398 0 : [this] {
399 0 : setNextView(_currentViewIndex + 1);
400 0 : });
401 0 : } else {
402 0 : if (from != Vec2(0, yPos)) {
403 0 : action = Rc<Sequence>::create(
404 0 : ActionAcceleratedMove::createBounce(5000, from, Vec2(0, yPos), Vec2(velocity.x, 0), 50000,
405 0 : std::bind(&MultiViewLayout::onSwipeAction, this, std::placeholders::_1)),
406 0 : [this] {
407 0 : _swipeProgress = 0;
408 0 : onSwipeProgress();
409 0 : });
410 : } else {
411 0 : _swipeProgress = 0;
412 0 : onSwipeProgress();
413 0 : onViewSelected(_currentView, _currentViewIndex);
414 : }
415 : }
416 0 : if (action) {
417 0 : _currentView->runAction(action, "ListAction"_tag);
418 : }
419 0 : return true;
420 0 : }
421 :
422 0 : void MultiViewLayout::onSwipeAction(Node *node) {
423 0 : if (node == _currentView) {
424 0 : _swipeProgress = _currentView->getPosition().x;
425 0 : onSwipeProgress();
426 : }
427 0 : }
428 :
429 0 : void MultiViewLayout::setNextView(int64_t id, float newProgress) {
430 0 : _currentViewIndex = id;
431 0 : if (_nextView) {
432 0 : _currentView = _nextView;
433 0 : _nextView = nullptr;
434 :
435 0 : onViewSelected(_currentView, id);
436 0 : setBaseNode(_currentView, ZOrder(1));
437 : }
438 0 : _swipeProgress = newProgress;
439 0 : onSwipeProgress();
440 0 : }
441 :
442 0 : void MultiViewLayout::setPrevView(int64_t id, float newProgress) {
443 0 : _currentViewIndex = id;
444 0 : if (_prevView) {
445 0 : _currentView = _prevView;
446 0 : _prevView = nullptr;
447 :
448 0 : onViewSelected(_currentView, id);
449 0 : setBaseNode(_currentView, ZOrder(1));
450 : }
451 0 : _swipeProgress = newProgress;
452 0 : onSwipeProgress();
453 0 : }
454 :
455 0 : void MultiViewLayout::onViewSelected(ScrollView *current, int64_t id) {
456 0 : _generator->onViewSelected(current, id);
457 0 : }
458 :
459 0 : void MultiViewLayout::onPrevViewCreated(ScrollView *current, ScrollView *prev) {
460 0 : prev->setContentSize(current->getContentSize());
461 0 : prev->setPosition(current->getPosition());
462 0 : prev->setPadding(current->getPadding());
463 0 : _generator->onViewCreated(prev, _currentViewIndex - 1);
464 0 : }
465 :
466 0 : void MultiViewLayout::onNextViewCreated(ScrollView *current, ScrollView *next) {
467 0 : next->setContentSize(current->getContentSize());
468 0 : next->setPosition(current->getPosition());
469 0 : next->setPadding(current->getPadding());
470 0 : _generator->onViewCreated(next, _currentViewIndex + 1);
471 0 : }
472 :
473 0 : void MultiViewLayout::applyView(ScrollView *current) {
474 0 : _generator->onApplyView(current, _currentViewIndex);
475 0 : }
476 :
477 0 : void MultiViewLayout::applyViewProgress(ScrollView *current, ScrollView *n, float p) {
478 0 : _generator->onApplyProgress(current, n, p);
479 0 : }
480 :
481 0 : void MultiViewLayout::onBaseNode(const NodeParams ¶ms, const Padding &padding, float offset) {
482 0 : FlexibleLayout::onBaseNode(params, padding, offset);
483 :
484 0 : if (_swipeProgress != 0.0f) {
485 0 : _currentView->setPositionX(_swipeProgress);
486 : }
487 0 : }
488 :
489 : }
|