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 "XLInputListener.h"
24 : #include "XL2dActionAcceleratedMove.h"
25 : #include "XL2dImageLayer.h"
26 : #include "XLTexture.h"
27 :
28 : namespace STAPPLER_VERSIONIZED stappler::xenolith::basic2d {
29 :
30 0 : Rect ImageLayer::getCorrectRect(Size2 containerSize) {
31 0 : Size2 parentSize = getContentSize();
32 0 : Rect ret = Rect(parentSize.width - containerSize.width,
33 0 : parentSize.height - containerSize.height,
34 0 : containerSize.width - parentSize.width,
35 0 : containerSize.height - parentSize.height);
36 :
37 0 : if (containerSize.width <= parentSize.width) {
38 0 : ret.origin.x = (parentSize.width - containerSize.width) / 2;
39 0 : ret.size.width = 0;
40 : }
41 :
42 0 : if (containerSize.height <= parentSize.height) {
43 0 : ret.origin.y = (parentSize.height - containerSize.height) / 2;
44 0 : ret.size.height = 0;
45 : }
46 :
47 0 : if (isnan(containerSize.width) || isnan(containerSize.height)) {
48 0 : log::format(log::LogType::Error, "ImageLayer", "rect %f %f %f %f : %f %f %f %f",
49 0 : parentSize.width, parentSize.height, containerSize.width, containerSize.height,
50 0 : ret.origin.x, ret.origin.y, ret.size.width, ret.size.height);
51 : }
52 :
53 0 : return ret;
54 : }
55 :
56 0 : Vec2 ImageLayer::getCorrectPosition(Size2 containerSize, Vec2 point) {
57 0 : Vec2 ret = point;
58 0 : Rect bounds = getCorrectRect(containerSize);
59 :
60 0 : if (ret.x < bounds.origin.x) {
61 0 : ret.x = bounds.origin.x;
62 0 : } else if (ret.x > bounds.getMaxX()) {
63 0 : ret.x = bounds.getMaxX();
64 : }
65 :
66 0 : if (ret.y < bounds.origin.y) {
67 0 : ret.y = bounds.origin.y;
68 0 : } else if (ret.y > bounds.getMaxY()) {
69 0 : ret.y = bounds.getMaxY();
70 : }
71 :
72 0 : if (isnan(ret.x) || isnan(ret.y)) {
73 0 : log::format(log::LogType::Error, "ImageLayer", "pos %f %f %f %f : %f %f : %f %f",
74 0 : bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height,
75 0 : point.x, point.y, ret.x, ret.y);
76 : }
77 0 : return ret;
78 : }
79 :
80 0 : Size2 ImageLayer::getContainerSize() const {
81 0 : return Size2(
82 0 : _root->getContentSize().width * _root->getScale().x,
83 0 : _root->getContentSize().height * _root->getScale().y);
84 : }
85 :
86 0 : Size2 ImageLayer::getContainerSizeForScale(float value) const {
87 0 : return Size2(
88 0 : _root->getContentSize().width * value,
89 0 : _root->getContentSize().height * value);
90 : }
91 :
92 0 : ImageLayer::~ImageLayer() { }
93 :
94 0 : bool ImageLayer::init() {
95 0 : if (!Node::init()) {
96 0 : return false;
97 : }
98 :
99 0 : setOpacity(1.0f);
100 0 : _gestureListener = addInputListener(Rc<InputListener>::create());
101 0 : _gestureListener->setTouchFilter([] (const InputEvent &ev, const InputListener::DefaultEventFilter &f) {
102 0 : return f(ev);
103 : });
104 0 : _gestureListener->addTapRecognizer([this] (const GestureTap &tap) {
105 0 : if (_actionCallback) {
106 0 : _actionCallback();
107 : }
108 0 : return handleTap(tap.input->currentLocation, tap.count);
109 : });
110 0 : _gestureListener->addSwipeRecognizer([this] (const GestureSwipe &s) {
111 0 : if (s.event == GestureEvent::Began) {
112 0 : if (_actionCallback) {
113 0 : _actionCallback();
114 : }
115 0 : return handleSwipeBegin(s.input->currentLocation);
116 0 : } else if (s.event == GestureEvent::Activated) {
117 0 : return handleSwipe(Vec2(s.delta.x / _globalScale.x, s.delta.y / _globalScale.y));
118 0 : } else if (s.event == GestureEvent::Ended) {
119 0 : return handleSwipeEnd(Vec2(s.velocity.x / _globalScale.x, s.velocity.y / _globalScale.y));
120 :
121 : }
122 0 : return true;
123 : });
124 0 : _gestureListener->addPinchRecognizer([this] (const GesturePinch &p) {
125 0 : if (p.event == GestureEvent::Began) {
126 0 : if (_actionCallback) {
127 0 : _actionCallback();
128 : }
129 0 : _hasPinch = true;
130 0 : } else if (p.event == GestureEvent::Activated) {
131 0 : return handlePinch(p.center, p.scale, p.velocity, false);
132 0 : } else if (p.event == GestureEvent::Ended || p.event == GestureEvent::Cancelled) {
133 0 : _hasPinch = false;
134 0 : return handlePinch(p.center, p.scale, p.velocity, true);
135 : }
136 0 : return true;
137 : });
138 :
139 0 : _root = addChild(Rc<Node>::create());
140 0 : _root->setCascadeOpacityEnabled(true);
141 0 : _root->setScale(1.0f);
142 :
143 0 : _image = _root->addChild(Rc<Sprite>::create(core::EmptyTextureName));
144 :
145 0 : _scaleSource = 0;
146 :
147 0 : return true;
148 : }
149 :
150 0 : void ImageLayer::onContentSizeDirty() {
151 0 : Node::onContentSizeDirty();
152 :
153 0 : auto imageSize = _image->getTexture()->getExtent();
154 0 : if (_imageSizePredefined) {
155 0 : imageSize = Extent3(_imageSize.width, _imageSize.height, 1);
156 : }
157 0 : _root->setContentSize(Size2(imageSize.width, imageSize.height));
158 :
159 0 : if (!_scaleDisabled) {
160 0 : _minScale = std::min(
161 0 : _contentSize.width / _image->getContentSize().width,
162 0 : _contentSize.height / _image->getContentSize().height);
163 :
164 0 : _maxScale = std::max(
165 0 : _image->getContentSize().width * GetMaxScaleFactor() / _contentSize.width,
166 0 : _image->getContentSize().height * GetMaxScaleFactor() / _contentSize.height);
167 : } else {
168 0 : _minScale = _maxScale = 1.0f;
169 : }
170 :
171 0 : if (_textureDirty) {
172 0 : _textureDirty = false;
173 0 : _root->setScale(_minScale);
174 : }
175 :
176 0 : Vec2 prevCenter = Vec2(_prevContentSize.width / 2.0f, _prevContentSize.height / 2.0f);
177 0 : Vec2 center = Vec2(_contentSize.width / 2.0f, _contentSize.height / 2.0f);
178 0 : Vec2 offset = center - prevCenter;
179 :
180 0 : _root->setPosition(getCorrectPosition(getContainerSize(), _root->getPosition().xy() + offset));
181 :
182 0 : auto currentScale = _root->getScale().x;
183 0 : if (_maxScale != 0 && _minScale != 0 && (currentScale > _maxScale || currentScale < _minScale)) {
184 0 : float newScale = currentScale;
185 0 : if (_minScale > _maxScale) {
186 0 : newScale = _minScale;
187 0 : } else if (currentScale < _minScale) {
188 0 : newScale = _minScale;
189 0 : } else if (currentScale > _maxScale) {
190 0 : newScale = _maxScale;
191 : }
192 0 : Vec2 pos = _root->getPosition().xy();
193 0 : Vec2 locInParent = Vec2(_contentSize.width / 2.0f, _contentSize.height / 2.0f);
194 :
195 0 : Vec2 normal = (pos - locInParent) / currentScale * newScale;
196 :
197 0 : _root->setScale(newScale);
198 0 : _root->setPosition(getCorrectPosition(getContainerSize(), locInParent + normal));
199 : }
200 :
201 0 : _prevContentSize = _contentSize;
202 0 : _root->setPosition(getCorrectPosition(getContainerSize(), _root->getPosition().xy()));
203 0 : }
204 :
205 0 : void ImageLayer::onTransformDirty(const Mat4 &parentTransform) {
206 0 : Node::onTransformDirty(parentTransform);
207 0 : Vec3 scale;
208 0 : getNodeToWorldTransform().getScale(&scale);
209 0 : _globalScale = Vec2(scale.x, scale.y);
210 0 : }
211 :
212 0 : void ImageLayer::setTexture(Rc<Texture> &&tex) {
213 0 : _imageSizePredefined = false;
214 :
215 0 : auto extent = tex->getExtent();
216 0 : _image->setTexture(move(tex));
217 0 : _image->setContentSize(Size2(extent.width, extent.height));
218 :
219 0 : if (_contentSize.width == 0.0f || _contentSize.height == 0.0f) {
220 0 : _minScale = 1.0f;
221 0 : _maxScale = 1.0f;
222 0 : _root->setScale(1.0f);
223 0 : _contentSizeDirty = true;
224 0 : _textureDirty = true;
225 0 : return;
226 : }
227 :
228 0 : if (_running) {
229 0 : if (!_scaleDisabled) {
230 0 : _minScale = std::min(
231 0 : _contentSize.width / _image->getContentSize().width,
232 0 : _contentSize.height / _image->getContentSize().height);
233 :
234 0 : _maxScale = std::max(
235 0 : _image->getContentSize().width * GetMaxScaleFactor() / _contentSize.width,
236 0 : _image->getContentSize().height * GetMaxScaleFactor() / _contentSize.height);
237 :
238 0 : _root->setScale(_minScale);
239 : } else {
240 0 : _minScale = _maxScale = 1.0f;
241 0 : Size2 imageSize = _image->getBoundingBox().size;
242 0 : _root->setContentSize(imageSize);
243 0 : _root->setScale(1.0f);
244 0 : _root->setPosition(getCorrectPosition(getContainerSize(),
245 0 : Vec2((_contentSize.width - imageSize.width) / 2.0f, _contentSize.height - imageSize.height)));
246 : }
247 :
248 0 : _contentSizeDirty = true;
249 : } else {
250 0 : _textureDirty = true;
251 : }
252 : }
253 :
254 0 : const Rc<Texture> &ImageLayer::getTexture() const {
255 0 : return _image->getTexture();
256 : }
257 :
258 0 : void ImageLayer::setActionCallback(Function<void()> &&cb) {
259 0 : _actionCallback = move(cb);
260 0 : }
261 :
262 0 : Vec2 ImageLayer::getTexturePosition() const {
263 0 : auto csize = getContainerSize();
264 0 : auto bsize = getContentSize();
265 0 : auto vec = _root->getPosition();
266 0 : bsize = Size2(csize.width - bsize.width, csize.height - bsize.height);
267 0 : Vec2 result;
268 0 : if (bsize.width <= 0) {
269 0 : bsize.width = 0;
270 0 : result.x = nan();
271 : } else {
272 0 : result.x = fabs(-vec.x / bsize.width);
273 : }
274 0 : if (bsize.height <= 0) {
275 0 : bsize.height = 0;
276 0 : result.y = nan();
277 : } else {
278 0 : result.y = fabs(-vec.y / bsize.height);
279 : }
280 :
281 0 : return result;
282 : }
283 :
284 0 : void ImageLayer::setScaleDisabled(bool value) {
285 0 : if (_scaleDisabled != value) {
286 0 : _scaleDisabled = value;
287 0 : _contentSizeDirty = true;
288 : }
289 0 : }
290 :
291 0 : void ImageLayer::setImageSize(Size2 size) {
292 0 : _imageSize = size;
293 0 : _imageSizePredefined = true;
294 0 : _contentSizeDirty = true;
295 0 : _textureDirty = true;
296 0 : }
297 :
298 0 : bool ImageLayer::handleTap(Vec2 point, int count) {
299 0 : if (count == 2 && !_scaleDisabled) {
300 0 : if (_root->getScale().x > _minScale) {
301 0 : Vec2 location = convertToNodeSpace(point);
302 :
303 0 : float newScale = _minScale;
304 0 : float origScale = _root->getScale().x;
305 0 : Vec2 pos = _root->getPosition().xy();
306 0 : Vec2 locInParent = convertToNodeSpace(location);
307 :
308 0 : Vec2 normal = (pos - locInParent) / origScale * newScale;
309 0 : Vec2 newPos = getCorrectPosition(getContainerSizeForScale(newScale), locInParent + normal);
310 :
311 0 : _root->runAction(Rc<Spawn>::create(
312 0 : Rc<ScaleTo>::create(0.35, newScale),
313 0 : Rc<MoveTo>::create(0.35, newPos)));
314 : } else {
315 0 : Vec2 location = convertToNodeSpace(point);
316 :
317 0 : float newScale = _minScale * 2.0f * _inputDensity;
318 0 : float origScale = _root->getScale().x;
319 :
320 0 : if (_minScale > _maxScale) {
321 0 : newScale = _minScale;
322 0 : } else if (newScale < _minScale) {
323 0 : newScale = _minScale;
324 0 : } else if (newScale > _maxScale) {
325 0 : newScale = _maxScale;
326 : }
327 :
328 0 : Vec2 pos = _root->getPosition().xy();
329 0 : Vec2 locInParent = convertToNodeSpace(location);
330 :
331 0 : Vec2 normal = (pos - locInParent) * (newScale / origScale) * _inputDensity;
332 0 : Vec2 newPos = getCorrectPosition(getContainerSizeForScale(newScale), locInParent + normal);
333 :
334 0 : _root->runAction(Rc<Spawn>::create(
335 0 : Rc<ScaleTo>::create(0.35, newScale),
336 0 : Rc<MoveTo>::create(0.35, newPos)));
337 : }
338 : }
339 0 : return true;
340 : }
341 :
342 0 : bool ImageLayer::handleSwipeBegin(Vec2 point) {
343 0 : return true;
344 : }
345 :
346 0 : bool ImageLayer::handleSwipe(Vec2 delta) {
347 0 : Vec2 containerPosition = _root->getPosition().xy();
348 0 : _root->stopAllActions();
349 0 : _root->setPosition(getCorrectPosition(getContainerSize(), containerPosition + delta));
350 0 : return true;
351 : }
352 :
353 0 : bool ImageLayer::handleSwipeEnd(Vec2 velocity) {
354 0 : _root->stopAllActions();
355 0 : auto a = ActionAcceleratedMove::createWithBounds(5000, _root->getPosition().xy(), velocity, getCorrectRect(_root->getBoundingBox().size));
356 0 : if (a) {
357 0 : _root->runAction(Rc<Sequence>::create(move(a), [this] {
358 0 : _contentSizeDirty = true;
359 0 : }));
360 : }
361 0 : return true;
362 0 : }
363 :
364 0 : bool ImageLayer::handlePinch(Vec2 location, float scale, float velocity, bool isEnded) {
365 0 : if (isEnded) {
366 0 : _contentSizeDirty = true;
367 0 : _scaleSource = 0;
368 0 : return true;
369 0 : } else if (_scaleSource == 0) {
370 0 : _scaleSource = _root->getScale().x;
371 : }
372 :
373 0 : if (_maxScale < _minScale) {
374 0 : return true;
375 : }
376 :
377 0 : float newScale = _scaleSource * scale;
378 0 : if (newScale < _minScale) {
379 0 : newScale = _minScale;
380 : }
381 0 : if (newScale > _maxScale && _maxScale > _minScale) {
382 0 : newScale = _maxScale;
383 : }
384 :
385 0 : float origScale = _root->getScale().x;
386 0 : Vec2 pos = _root->getPosition().xy();
387 0 : Vec2 locInParent = convertToNodeSpace(location);
388 :
389 0 : Vec2 normal = (pos - locInParent) / origScale * newScale;
390 :
391 0 : _root->setScale(newScale);
392 0 : _root->setPosition(getCorrectPosition(getContainerSize(), locInParent + normal));
393 :
394 0 : return true;
395 : }
396 :
397 : }
|