1
|
|
2
|
Copyright (c) 2008-2010 Ricardo Quesada
|
3
|
Copyright (c) 2010-2012 cocos2d-x.org
|
4
|
Copyright (c) 2013-2014 Chukong Technologies Inc.
|
5
|
|
6
|
http://www.cocos2d-x.org
|
7
|
|
8
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
of this software and associated documentation files (the "Software"), to deal
|
10
|
in the Software without restriction, including without limitation the rights
|
11
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
copies of the Software, and to permit persons to whom the Software is
|
13
|
furnished to do so, subject to the following conditions:
|
14
|
|
15
|
The above copyright notice and this permission notice shall be included in
|
16
|
all copies or substantial portions of the Software.
|
17
|
|
18
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
THE SOFTWARE.
|
25
|
****************************************************************************/
|
26
|
#include "CCMenu.h"
|
27
|
#include "CCDirector.h"
|
28
|
#include "CCApplication.h"
|
29
|
#include "CCTouch.h"
|
30
|
#include "CCStdC.h"
|
31
|
#include "CCInteger.h"
|
32
|
#include "CCEventListenerTouch.h"
|
33
|
#include "CCString.h"
|
34
|
|
35
|
#include <vector>
|
36
|
#include <stdarg.h>
|
37
|
|
38
|
using namespace std;
|
39
|
|
40
|
NS_CC_BEGIN
|
41
|
|
42
|
enum
|
43
|
{
|
44
|
kDefaultPadding = 5,
|
45
|
};
|
46
|
|
47
|
|
48
|
|
49
|
|
50
|
|
51
|
Menu::~Menu()
|
52
|
{
|
53
|
CCLOGINFO("In the destructor of Menu. %p", this);
|
54
|
}
|
55
|
|
56
|
Menu* Menu::create()
|
57
|
{
|
58
|
return Menu::create(nullptr, nullptr);
|
59
|
}
|
60
|
|
61
|
Menu * Menu::create(MenuItem* item, ...)
|
62
|
{
|
63
|
va_list args;
|
64
|
va_start(args,item);
|
65
|
|
66
|
Menu *ret = Menu::createWithItems(item, args);
|
67
|
|
68
|
va_end(args);
|
69
|
|
70
|
return ret;
|
71
|
}
|
72
|
|
73
|
Menu* Menu::createWithArray(const Vector<MenuItem*>& arrayOfItems)
|
74
|
{
|
75
|
auto ret = new Menu();
|
76
|
if (ret && ret->initWithArray(arrayOfItems))
|
77
|
{
|
78
|
ret->autorelease();
|
79
|
}
|
80
|
else
|
81
|
{
|
82
|
CC_SAFE_DELETE(ret);
|
83
|
}
|
84
|
|
85
|
return ret;
|
86
|
}
|
87
|
|
88
|
Menu* Menu::createWithItems(MenuItem* item, va_list args)
|
89
|
{
|
90
|
Vector<MenuItem*> items;
|
91
|
if( item )
|
92
|
{
|
93
|
items.pushBack(item);
|
94
|
MenuItem *i = va_arg(args, MenuItem*);
|
95
|
while(i)
|
96
|
{
|
97
|
items.pushBack(i);
|
98
|
i = va_arg(args, MenuItem*);
|
99
|
}
|
100
|
}
|
101
|
|
102
|
return Menu::createWithArray(items);
|
103
|
}
|
104
|
|
105
|
Menu* Menu::createWithItem(MenuItem* item)
|
106
|
{
|
107
|
return Menu::create(item, nullptr);
|
108
|
}
|
109
|
|
110
|
bool Menu::init()
|
111
|
{
|
112
|
return initWithArray(Vector<MenuItem*>());
|
113
|
}
|
114
|
|
115
|
bool Menu::initWithArray(const Vector<MenuItem*>& arrayOfItems)
|
116
|
{
|
117
|
if (Layer::init())
|
118
|
{
|
119
|
_enabled = true;
|
120
|
|
121
|
Size s = Director::getInstance()->getWinSize();
|
122
|
|
123
|
this->ignoreAnchorPointForPosition(true);
|
124
|
setAnchorPoint(Point(0.5f, 0.5f));
|
125
|
this->setContentSize(s);
|
126
|
|
127
|
setPosition(Point(s.width/2, s.height/2));
|
128
|
|
129
|
int z=0;
|
130
|
|
131
|
for (auto& item : arrayOfItems)
|
132
|
{
|
133
|
this->addChild(item, z);
|
134
|
z++;
|
135
|
}
|
136
|
|
137
|
_selectedItem = nullptr;
|
138
|
_state = Menu::State::WAITING;
|
139
|
|
140
|
|
141
|
setCascadeColorEnabled(true);
|
142
|
setCascadeOpacityEnabled(true);
|
143
|
|
144
|
|
145
|
addTouchEvent();
|
146
|
|
147
|
return true;
|
148
|
}
|
149
|
return false;
|
150
|
}
|
151
|
|
152
|
|
153
|
* adds even listener for Menu
|
154
|
*/
|
155
|
|
156
|
void Menu::addTouchEvent()
|
157
|
{
|
158
|
auto touchListener = EventListenerTouchOneByOne::create();
|
159
|
touchListener->setSwallowTouches(true);
|
160
|
|
161
|
touchListener->onTouchBegan = CC_CALLBACK_2(Menu::onTouchBegan, this);
|
162
|
touchListener->onTouchMoved = CC_CALLBACK_2(Menu::onTouchMoved, this);
|
163
|
touchListener->onTouchEnded = CC_CALLBACK_2(Menu::onTouchEnded, this);
|
164
|
touchListener->onTouchCancelled = CC_CALLBACK_2(Menu::onTouchCancelled, this);
|
165
|
|
166
|
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
|
167
|
}
|
168
|
|
169
|
|
170
|
|
171
|
* override add:
|
172
|
*/
|
173
|
void Menu::addChild(Node * child)
|
174
|
{
|
175
|
Layer::addChild(child);
|
176
|
}
|
177
|
|
178
|
void Menu::addChild(Node * child, int zOrder)
|
179
|
{
|
180
|
Layer::addChild(child, zOrder);
|
181
|
}
|
182
|
|
183
|
void Menu::addChild(Node * child, int zOrder, int tag)
|
184
|
{
|
185
|
CCASSERT( dynamic_cast<MenuItem*>(child) != nullptr, "Menu only supports MenuItem objects as children");
|
186
|
Layer::addChild(child, zOrder, tag);
|
187
|
}
|
188
|
|
189
|
void Menu::onEnter()
|
190
|
{
|
191
|
Layer::onEnter();
|
192
|
}
|
193
|
|
194
|
void Menu::onExit()
|
195
|
{
|
196
|
if (_state == Menu::State::TRACKING_TOUCH)
|
197
|
{
|
198
|
if (_selectedItem)
|
199
|
{
|
200
|
_selectedItem->unselected();
|
201
|
_selectedItem = nullptr;
|
202
|
}
|
203
|
|
204
|
_state = Menu::State::WAITING;
|
205
|
}
|
206
|
|
207
|
Layer::onExit();
|
208
|
}
|
209
|
|
210
|
void Menu::removeChild(Node* child, bool cleanup)
|
211
|
{
|
212
|
MenuItem *menuItem = dynamic_cast<MenuItem*>(child);
|
213
|
CCASSERT(menuItem != nullptr, "Menu only supports MenuItem objects as children");
|
214
|
|
215
|
if (_selectedItem == menuItem)
|
216
|
{
|
217
|
_selectedItem = nullptr;
|
218
|
}
|
219
|
|
220
|
Node::removeChild(child, cleanup);
|
221
|
}
|
222
|
|
223
|
|
224
|
|
225
|
bool Menu::onTouchBegan(Touch* touch, Event* event)
|
226
|
{
|
227
|
if (_state != Menu::State::WAITING || ! _visible || !_enabled)
|
228
|
{
|
229
|
return false;
|
230
|
}
|
231
|
|
232
|
for (Node *c = this->_parent; c != nullptr; c = c->getParent())
|
233
|
{
|
234
|
if (c->isVisible() == false)
|
235
|
{
|
236
|
return false;
|
237
|
}
|
238
|
}
|
239
|
|
240
|
_selectedItem = this->getItemForTouch(touch);
|
241
|
if (_selectedItem)
|
242
|
{
|
243
|
_state = Menu::State::TRACKING_TOUCH;
|
244
|
_selectedItem->selected();
|
245
|
|
246
|
return true;
|
247
|
}
|
248
|
|
249
|
return false;
|
250
|
}
|
251
|
|
252
|
void Menu::onTouchEnded(Touch* touch, Event* event)
|
253
|
{
|
254
|
CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchEnded] -- invalid state");
|
255
|
this->retain();
|
256
|
if (_selectedItem)
|
257
|
{
|
258
|
_selectedItem->unselected();
|
259
|
_selectedItem->activate();
|
260
|
}
|
261
|
_state = Menu::State::WAITING;
|
262
|
this->release();
|
263
|
}
|
264
|
|
265
|
void Menu::onTouchCancelled(Touch* touch, Event* event)
|
266
|
{
|
267
|
CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchCancelled] -- invalid state");
|
268
|
this->retain();
|
269
|
if (_selectedItem)
|
270
|
{
|
271
|
_selectedItem->unselected();
|
272
|
}
|
273
|
_state = Menu::State::WAITING;
|
274
|
this->release();
|
275
|
}
|
276
|
|
277
|
void Menu::onTouchMoved(Touch* touch, Event* event)
|
278
|
{
|
279
|
CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchMoved] -- invalid state");
|
280
|
MenuItem *currentItem = this->getItemForTouch(touch);
|
281
|
if (currentItem != _selectedItem)
|
282
|
{
|
283
|
if (_selectedItem)
|
284
|
{
|
285
|
_selectedItem->unselected();
|
286
|
}
|
287
|
_selectedItem = currentItem;
|
288
|
if (_selectedItem)
|
289
|
{
|
290
|
_selectedItem->selected();
|
291
|
}
|
292
|
}
|
293
|
}
|
294
|
|
295
|
|
296
|
void Menu::alignItemsVertically()
|
297
|
{
|
298
|
this->alignItemsVerticallyWithPadding(kDefaultPadding);
|
299
|
}
|
300
|
|
301
|
void Menu::alignItemsVerticallyWithPadding(float padding)
|
302
|
{
|
303
|
float height = -padding;
|
304
|
|
305
|
for(const auto &child : _children)
|
306
|
height += child->getContentSize().height * child->getScaleY() + padding;
|
307
|
|
308
|
float y = height / 2.0f;
|
309
|
|
310
|
for(const auto &child : _children) {
|
311
|
child->setPosition(Point(0, y - child->getContentSize().height * child->getScaleY() / 2.0f));
|
312
|
y -= child->getContentSize().height * child->getScaleY() + padding;
|
313
|
}
|
314
|
}
|
315
|
|
316
|
void Menu::alignItemsHorizontally(void)
|
317
|
{
|
318
|
this->alignItemsHorizontallyWithPadding(kDefaultPadding);
|
319
|
}
|
320
|
|
321
|
void Menu::alignItemsHorizontallyWithPadding(float padding)
|
322
|
{
|
323
|
float width = -padding;
|
324
|
for(const auto &child : _children)
|
325
|
width += child->getContentSize().width * child->getScaleX() + padding;
|
326
|
|
327
|
float x = -width / 2.0f;
|
328
|
|
329
|
for(const auto &child : _children) {
|
330
|
child->setPosition(Point(x + child->getContentSize().width * child->getScaleX() / 2.0f, 0));
|
331
|
x += child->getContentSize().width * child->getScaleX() + padding;
|
332
|
}
|
333
|
}
|
334
|
|
335
|
void Menu::alignItemsInColumns(int columns, ...)
|
336
|
{
|
337
|
va_list args;
|
338
|
va_start(args, columns);
|
339
|
|
340
|
this->alignItemsInColumns(columns, args);
|
341
|
|
342
|
va_end(args);
|
343
|
}
|
344
|
|
345
|
void Menu::alignItemsInColumns(int columns, va_list args)
|
346
|
{
|
347
|
CCASSERT(columns >= 0, "Columns must be >= 0");
|
348
|
ValueVector rows;
|
349
|
while (columns)
|
350
|
{
|
351
|
rows.push_back(Value(columns));
|
352
|
columns = va_arg(args, int);
|
353
|
}
|
354
|
alignItemsInColumnsWithArray(rows);
|
355
|
}
|
356
|
|
357
|
void Menu::alignItemsInColumnsWithArray(const ValueVector& rows)
|
358
|
{
|
359
|
int height = -5;
|
360
|
size_t row = 0;
|
361
|
int rowHeight = 0;
|
362
|
int columnsOccupied = 0;
|
363
|
int rowColumns = 0;
|
364
|
|
365
|
for(const auto &child : _children) {
|
366
|
CCASSERT(row < rows.size(), "");
|
367
|
|
368
|
rowColumns = rows[row].asInt();
|
369
|
|
370
|
CCASSERT(rowColumns, "");
|
371
|
|
372
|
float tmp = child->getContentSize().height;
|
373
|
rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp);
|
374
|
|
375
|
++columnsOccupied;
|
376
|
if (columnsOccupied >= rowColumns)
|
377
|
{
|
378
|
height += rowHeight + 5;
|
379
|
|
380
|
columnsOccupied = 0;
|
381
|
rowHeight = 0;
|
382
|
++row;
|
383
|
}
|
384
|
}
|
385
|
|
386
|
|
387
|
CCASSERT(! columnsOccupied, "");
|
388
|
|
389
|
Size winSize = Director::getInstance()->getWinSize();
|
390
|
|
391
|
row = 0;
|
392
|
rowHeight = 0;
|
393
|
rowColumns = 0;
|
394
|
float w = 0.0;
|
395
|
float x = 0.0;
|
396
|
float y = (float)(height / 2);
|
397
|
|
398
|
for(const auto &child : _children) {
|
399
|
if (rowColumns == 0)
|
400
|
{
|
401
|
rowColumns = rows[row].asInt();
|
402
|
w = winSize.width / (1 + rowColumns);
|
403
|
x = w;
|
404
|
}
|
405
|
|
406
|
float tmp = child->getContentSize().height;
|
407
|
rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp);
|
408
|
|
409
|
child->setPosition(Point(x - winSize.width / 2,
|
410
|
y - child->getContentSize().height / 2));
|
411
|
|
412
|
x += w;
|
413
|
++columnsOccupied;
|
414
|
|
415
|
if (columnsOccupied >= rowColumns)
|
416
|
{
|
417
|
y -= rowHeight + 5;
|
418
|
|
419
|
columnsOccupied = 0;
|
420
|
rowColumns = 0;
|
421
|
rowHeight = 0;
|
422
|
++row;
|
423
|
}
|
424
|
}
|
425
|
}
|
426
|
|
427
|
void Menu::alignItemsInRows(int rows, ...)
|
428
|
{
|
429
|
va_list args;
|
430
|
va_start(args, rows);
|
431
|
|
432
|
this->alignItemsInRows(rows, args);
|
433
|
|
434
|
va_end(args);
|
435
|
}
|
436
|
|
437
|
void Menu::alignItemsInRows(int rows, va_list args)
|
438
|
{
|
439
|
ValueVector array;
|
440
|
while (rows)
|
441
|
{
|
442
|
array.push_back(Value(rows));
|
443
|
rows = va_arg(args, int);
|
444
|
}
|
445
|
alignItemsInRowsWithArray(array);
|
446
|
}
|
447
|
|
448
|
void Menu::alignItemsInRowsWithArray(const ValueVector& columns)
|
449
|
{
|
450
|
vector<int> columnWidths;
|
451
|
vector<int> columnHeights;
|
452
|
|
453
|
int width = -10;
|
454
|
int columnHeight = -5;
|
455
|
size_t column = 0;
|
456
|
int columnWidth = 0;
|
457
|
int rowsOccupied = 0;
|
458
|
int columnRows;
|
459
|
|
460
|
for(const auto &child : _children) {
|
461
|
|
462
|
CCASSERT(column < columns.size(), "");
|
463
|
|
464
|
columnRows = columns[column].asInt();
|
465
|
|
466
|
CCASSERT(columnRows, "");
|
467
|
|
468
|
|
469
|
float tmp = child->getContentSize().width;
|
470
|
columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);
|
471
|
|
472
|
columnHeight += (int)(child->getContentSize().height + 5);
|
473
|
++rowsOccupied;
|
474
|
|
475
|
if (rowsOccupied >= columnRows)
|
476
|
{
|
477
|
columnWidths.push_back(columnWidth);
|
478
|
columnHeights.push_back(columnHeight);
|
479
|
width += columnWidth + 10;
|
480
|
|
481
|
rowsOccupied = 0;
|
482
|
columnWidth = 0;
|
483
|
columnHeight = -5;
|
484
|
++column;
|
485
|
}
|
486
|
}
|
487
|
|
488
|
|
489
|
CCASSERT(! rowsOccupied, "");
|
490
|
|
491
|
Size winSize = Director::getInstance()->getWinSize();
|
492
|
|
493
|
column = 0;
|
494
|
columnWidth = 0;
|
495
|
columnRows = 0;
|
496
|
float x = (float)(-width / 2);
|
497
|
float y = 0.0;
|
498
|
|
499
|
for(const auto &child : _children) {
|
500
|
if (columnRows == 0)
|
501
|
{
|
502
|
columnRows = columns[column].asInt();
|
503
|
y = (float) columnHeights[column];
|
504
|
}
|
505
|
|
506
|
|
507
|
float tmp = child->getContentSize().width;
|
508
|
columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);
|
509
|
|
510
|
child->setPosition(Point(x + columnWidths[column] / 2,
|
511
|
y - winSize.height / 2));
|
512
|
|
513
|
y -= child->getContentSize().height + 10;
|
514
|
++rowsOccupied;
|
515
|
|
516
|
if (rowsOccupied >= columnRows)
|
517
|
{
|
518
|
x += columnWidth + 5;
|
519
|
rowsOccupied = 0;
|
520
|
columnRows = 0;
|
521
|
columnWidth = 0;
|
522
|
++column;
|
523
|
}
|
524
|
}
|
525
|
}
|
526
|
|
527
|
MenuItem* Menu::getItemForTouch(Touch *touch)
|
528
|
{
|
529
|
Point touchLocation = touch->getLocation();
|
530
|
|
531
|
if (!_children.empty())
|
532
|
{
|
533
|
for (auto iter = _children.crbegin(); iter != _children.crend(); ++iter)
|
534
|
{
|
535
|
MenuItem* child = dynamic_cast<MenuItem*>(*iter);
|
536
|
if (child && child->isVisible() && child->isEnabled())
|
537
|
{
|
538
|
Point local = child->convertToNodeSpace(touchLocation);
|
539
|
Rect r = child->rect();
|
540
|
r.origin = Point::ZERO;
|
541
|
|
542
|
if (r.containsPoint(local))
|
543
|
{
|
544
|
return child;
|
545
|
}
|
546
|
}
|
547
|
}
|
548
|
}
|
549
|
|
550
|
return nullptr;
|
551
|
}
|
552
|
|
553
|
std::string Menu::getDescription() const
|
554
|
{
|
555
|
return StringUtils::format("<Menu | Tag = %d>", _tag);
|
556
|
}
|
557
|
|
558
|
NS_CC_END
|