MyGUI  3.0.1
MyGUI_MenuCtrl.cpp
Go to the documentation of this file.
1 
7 /*
8  This file is part of MyGUI.
9 
10  MyGUI is free software: you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  MyGUI is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with MyGUI. If not, see <http://www.gnu.org/licenses/>.
22 */
23 #include "MyGUI_Precompiled.h"
24 #include "MyGUI_MenuCtrl.h"
25 #include "MyGUI_ResourceSkin.h"
26 #include "MyGUI_MenuItem.h"
27 #include "MyGUI_StaticImage.h"
28 #include "MyGUI_MenuBar.h"
29 #include "MyGUI_WidgetManager.h"
30 #include "MyGUI_LayerManager.h"
32 #include "MyGUI_InputManager.h"
33 #include "MyGUI_Gui.h"
34 
35 namespace MyGUI
36 {
37 
38  const float POPUP_MENU_SPEED_COEF = 3.0f;
39 
41  mHideByAccept(true),
42  mMenuDropMode(false),
43  mIsMenuDrop(true),
44  mHideByLostKey(false),
45  mHeightLine(1),
46  mSubmenuImageSize(0),
47  mShutdown(false),
48  mSeparatorHeight(0),
49  mAlignVert(true),
50  mDistanceButton(0),
51  mPopupAccept(false),
52  mOwner(nullptr),
53  mAnimateSmooth(false)
54  {
55  }
56 
57  void MenuCtrl::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
58  {
59  Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
60 
61  // инициализируем овнера
62  Widget* parent = getParent();
63  if (parent)
64  {
65  mOwner = parent->castType<MenuItem>(false);
66  if ( ! mOwner )
67  {
68  Widget* client = parent;
69  parent = client->getParent();
70  if (parent && parent->getClientWidget())
71  {
72  mOwner = parent->castType<MenuItem>(false);
73  }
74  }
75  }
76 
77  initialiseWidgetSkin(_info);
78  }
79 
81  {
82  mShutdown = true;
83  shutdownWidgetSkin();
84  }
85 
87  {
88  shutdownWidgetSkin();
90  initialiseWidgetSkin(_info);
91  }
92 
93  void MenuCtrl::initialiseWidgetSkin(ResourceSkin* _info)
94  {
95  // нам нужен фокус клавы
96  mNeedKeyFocus = true;
97 
98  for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
99  {
100  if (*(*iter)->_getInternalData<std::string>() == "Client")
101  {
102  MYGUI_DEBUG_ASSERT( ! mWidgetClient, "widget already assigned");
103  mWidgetClient = (*iter);
104  }
105  }
106  //MYGUI_ASSERT(nullptr != mWidgetClient, "Child Widget Client not found in skin (MenuCtrl must have Client)");
107 
108  // парсим свойства
109  const MapString& properties = _info->getProperties();
110  MapString::const_iterator iterS = properties.find("SkinLine");
111  if (iterS != properties.end()) mSkinLine = iterS->second;
112  //MYGUI_ASSERT(!mSkinLine.empty(), "SkinLine property not found (MenuCtrl must have SkinLine property)");
113 
114  iterS = properties.find("HeightLine");
115  if (iterS != properties.end()) mHeightLine = utility::parseInt(iterS->second);
116  if (mHeightLine < 1)
117  {
118  MYGUI_LOG(Warning, "MenuCtrl HeightLine can't be less thah 1. Set to 1.");
119  mHeightLine = 1;
120  }
121 
122  iterS = properties.find("SeparatorHeight");
123  if (iterS != properties.end()) mSeparatorHeight = utility::parseInt(iterS->second);
124  iterS = properties.find("SeparatorSkin");
125  if (iterS != properties.end()) mSeparatorSkin = iterS->second;
126 
127  iterS = properties.find("SubmenuImageSize");
128  if (iterS != properties.end()) mSubmenuImageSize = utility::parseInt(iterS->second);
129 
130  iterS = properties.find("SubMenuSkin");
131  if (iterS != properties.end()) mSubMenuSkin = iterS->second;
132  //MYGUI_ASSERT(!mSubMenuSkin.empty(), "SubMenuSkin property not found (MenuCtrl must have SubMenuSkin property)");
133 
134  iterS = properties.find("SubMenuLayer");
135  if (iterS != properties.end()) mSubMenuLayer = iterS->second;
136  //MYGUI_ASSERT(!mSubMenuLayer.empty(), "SubMenuLayer property not found (MenuCtrl must have SubMenuLayer property)");
137 
138  iterS = properties.find("AlignVert");
139  if (iterS != properties.end()) mAlignVert = utility::parseBool(iterS->second);
140  iterS = properties.find("DistanceButton");
141  if (iterS != properties.end()) mDistanceButton = utility::parseInt(iterS->second);
142 
143  if (mSeparatorHeight < 1) mSeparatorHeight = mHeightLine;
144  }
145 
146  void MenuCtrl::shutdownWidgetSkin()
147  {
148  mWidgetClient = nullptr;
149  }
150 
151  Widget* MenuCtrl::baseCreateWidget(WidgetStyle _style, const std::string& _type, const std::string& _skin, const IntCoord& _coord, Align _align, const std::string& _layer, const std::string& _name)
152  {
153  Widget* widget = nullptr;
154  if (mWidgetClient != nullptr)
155  widget = mWidgetClient->createWidgetT(_style, _type, _skin, _coord, _align, _layer, _name);
156  else
157  widget = Base::baseCreateWidget(_style, _type, _skin, _coord, _align, _layer, _name);
158 
159  MenuItem* child = widget->castType<MenuItem>(false);
160  if (child)
161  {
162  _wrapItem(child, mItemsInfo.size(), "", MenuItemType::Normal, "", Any::Null);
163  }
164  return widget;
165  }
166 
167  MenuItem* MenuCtrl::insertItemAt(size_t _index, const UString& _name, MenuItemType _type, const std::string& _id, Any _data)
168  {
169  MYGUI_ASSERT_RANGE_INSERT(_index, mItemsInfo.size(), "MenuCtrl::insertItemAt");
170  if (_index == ITEM_NONE) _index = mItemsInfo.size();
171 
172  MenuItem* item = _getClientWidget()->createWidget<MenuItem>(getSkinByType(_type), IntCoord(), Align::Default);
173  _wrapItem(item, _index, _name, _type, _id, _data);
174 
175  return item;
176  }
177 
178  void MenuCtrl::removeItemAt(size_t _index)
179  {
180  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::removeItemAt");
181 
182  if ( mItemsInfo[_index].submenu )
183  {
184  WidgetManager::getInstance().destroyWidget(mItemsInfo[_index].submenu);
185  }
186  WidgetManager::getInstance().destroyWidget(mItemsInfo[_index].item);
187  }
188 
190  {
191  while (mItemsInfo.size() > 0)
192  {
193  if ( mItemsInfo.back().submenu )
194  {
195  WidgetManager::getInstance().destroyWidget(mItemsInfo.back().submenu);
196  }
197  WidgetManager::getInstance().destroyWidget(mItemsInfo.back().item);
198  }
199  }
200 
201  const UString& MenuCtrl::getItemNameAt(size_t _index)
202  {
203  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::getItemNameAt");
204  return mItemsInfo[_index].name;
205  }
206 
207  void MenuCtrl::setButtonImageIndex(Button* _button, size_t _index)
208  {
209  StaticImage* image = _button->getStaticImage();
210  if ( nullptr == image ) return;
211  if (image->getItemResource())
212  {
213  static const size_t CountIcons = 2;
214  static const char * IconNames[CountIcons + 1] = { "None", "Popup", "" };
215  if (_index >= CountIcons) _index = CountIcons;
216  image->setItemName(IconNames[_index]);
217  }
218  else
219  {
220  image->setItemSelect(_index);
221  }
222  }
223 
224  void MenuCtrl::update()
225  {
226  IntSize size;
227 
228  if (mAlignVert)
229  {
230  for (VectorMenuItemInfo::iterator iter=mItemsInfo.begin(); iter!=mItemsInfo.end(); ++iter)
231  {
232  int height = iter->type == MenuItemType::Separator ? mSeparatorHeight : mHeightLine;
233  iter->item->setCoord(0, size.height, _getClientWidget()->getWidth(), height);
234  size.height += height + mDistanceButton;
235 
236  int width = iter->width;
237  if (width > size.width) size.width = width;
238  }
239 
240  }
241  else
242  {
243  for (VectorMenuItemInfo::iterator iter=mItemsInfo.begin(); iter!=mItemsInfo.end(); ++iter)
244  {
245  int width = iter->type == MenuItemType::Separator ? mSeparatorHeight : iter->width;
246  iter->item->setCoord(size.width, 0, width, mHeightLine);
247  size.width += width + mDistanceButton;
248  }
249  size.height = mHeightLine;
250  size.width = mCoord.width;
251  }
252 
253  setSize(size + mCoord.size() - _getClientWidget()->getSize());
254  }
255 
256  void MenuCtrl::setItemDataAt(size_t _index, Any _data)
257  {
258  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::setItemDataAt");
259  mItemsInfo[_index].data = _data;
260  }
261 
263  {
264  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::getItemChildAt");
265  return mItemsInfo[_index].submenu;
266  }
267 
268  void MenuCtrl::removeItemChildAt(size_t _index)
269  {
270  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::removeItemChildAt");
271 
272  if (mItemsInfo[_index].submenu != nullptr)
273  {
274  WidgetManager::getInstance().destroyWidget(mItemsInfo[_index].submenu);
275  mItemsInfo[_index].submenu = nullptr;
276  }
277 
278  update();
279  }
280 
281  void MenuCtrl::setItemNameAt(size_t _index, const UString& _name)
282  {
283  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::setItemNameAt");
284 
285  mItemsInfo[_index].name = _name;
286  MenuItem* item = mItemsInfo[_index].item;
287  item->setCaption(_name);
288 
289  update();
290  }
291 
292  void MenuCtrl::setItemIdAt(size_t _index, const std::string& _id)
293  {
294  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::setItemIdAt");
295  mItemsInfo[_index].id = _id;
296  }
297 
298  const std::string& MenuCtrl::getItemIdAt(size_t _index)
299  {
300  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::getItemIdAt");
301  return mItemsInfo[_index].id;
302  }
303 
305  {
306  // общий шутдаун виджета
307  if (mShutdown) return;
308 
309  size_t index = getItemIndex(_item);
310  mItemsInfo.erase(mItemsInfo.begin() + index);
311  update();
312  }
313 
315  {
316  size_t index = getItemIndex(_item);
317  mItemsInfo[index].name = _item->getCaption();
318 
319  ISubWidgetText* text = _item->getSubWidgetText();
320  mItemsInfo[index].width = text ? (text->getTextSize().width + _item->getSize().width - text->getWidth()) : 0;
321  update();
322  }
323 
325  {
326  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::getItemTypeAt");
327  return mItemsInfo[_index].type;
328  }
329 
330  void MenuCtrl::setItemTypeAt(size_t _index, MenuItemType _type)
331  {
332  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::setItemTypeAt");
333  ItemInfo& info = mItemsInfo[_index];
334  if (info.type == _type) return;
335 
336  // сохраняем данные
337  info.type = _type;
338  info.item->changeWidgetSkin(getSkinByType(_type));
339  setButtonImageIndex(info.item, getIconIndexByType(_type ));
340  info.item->setCaption(info.name);
341 
342  update();
343  }
344 
345  void MenuCtrl::notifyMenuCtrlAccept(MenuItem* _item)
346  {
347  Widget* sender = this;
348 
350  eventMenuCtrlAccept(this, _item);
352 
353  // нас удалили
354  if (sender == nullptr) return;
355 
357 
358  MenuItem* parent_item = getMenuItemParent();
359  if (parent_item)
360  {
361  MenuCtrl* parent_ctrl = parent_item->getMenuCtrlParent();
362  if (parent_ctrl)
363  {
364  parent_ctrl->notifyMenuCtrlAccept(_item);
365  }
366  }
367 
369 
370  // нас удалили
371  if (sender == nullptr) return;
372 
373 
374  if (mHideByAccept)
375  {
376  setVisibleSmooth(false);
377  }
378  else
379  {
381  }
382  }
383 
384  void MenuCtrl::setItemChildVisibleAt(size_t _index, bool _visible)
385  {
386  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::setItemChildVisibleAt");
387 
388  if (_visible)
389  {
390  if (mItemsInfo[_index].submenu && mItemsInfo[_index].submenu->getItemCount())
391  {
392 
393  int offset = mItemsInfo[0].item->getAbsoluteTop() - this->getAbsoluteTop();
394 
395  const IntCoord& coord = mItemsInfo[_index].item->getAbsoluteCoord();
396  IntPoint point(this->getAbsoluteRect().right, coord.top - offset);
397 
398  MenuCtrl* menu = mItemsInfo[_index].submenu;
399 
400  if (this->mAlignVert)
401  {
402  if (point.left + menu->getWidth() > MyGUI::Gui::getInstance().getViewSize().width)
403  point.left -= menu->getWidth();
404  if (point.top + menu->getHeight() > MyGUI::Gui::getInstance().getViewSize().height)
405  point.top -= menu->getHeight();
406  }
407  else
408  {
409  point.set(coord.left, this->getAbsoluteRect().bottom);
410  }
411 
412  menu->setPosition(point);
413  menu->setVisibleSmooth(true);
414  }
415  }
416  else
417  {
418  if (mItemsInfo[_index].submenu)
419  {
420  mItemsInfo[_index].submenu->setVisibleSmooth(false);
421  }
422  }
423  }
424 
425  void MenuCtrl::notifyRootKeyChangeFocus(Widget* _sender, bool _focus)
426  {
427  MenuItem* item = _sender->castType<MenuItem>();
428  if (item->getItemType() == MenuItemType::Popup)
429  {
430  if (_focus)
431  {
432  if (!mMenuDropMode || mIsMenuDrop)
433  {
434  item->setItemChildVisible(true);
435  item->setButtonPressed(true);
436  }
437  }
438  else
439  {
440  item->setItemChildVisible(false);
441  item->setButtonPressed(false);
442  }
443  }
444  }
445 
446  Widget* MenuCtrl::createItemChildByType(size_t _index, const std::string& _type)
447  {
448  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::createItemChildByType");
449  removeItemChildAt(_index);
450  Widget* child = mItemsInfo[_index].item->createWidgetT(WidgetStyle::Popup, _type, mSubMenuSkin, IntCoord(), Align::Default, mSubMenuLayer);
451  MYGUI_ASSERT(child->isType<MenuCtrl>(), "child must have MenuCtrl base type");
452  return child;
453  }
454 
455  void MenuCtrl::notifyMouseButtonClick(Widget* _sender)
456  {
457  MenuItem* item = _sender->castType<MenuItem>();
458  if (mMenuDropMode)
459  {
460  if (mIsMenuDrop)
461  {
462  if (item->getItemType() == MenuItemType::Popup)
463  {
464  item->setButtonPressed(false);
465  item->setItemChildVisible(false);
466  mIsMenuDrop = false;
467  }
468  }
469  else
470  {
471  if (item->getItemType() == MenuItemType::Popup)
472  {
473  mIsMenuDrop = true;
474  item->setButtonPressed(true);
475  item->setItemChildVisible(true);
477  }
478  }
479  }
480  else
481  {
482  if ((item->getItemType() == MenuItemType::Popup && mPopupAccept) ||
483  item->getItemType() == MenuItemType::Normal)
484  {
485  notifyMenuCtrlAccept(item);
486  }
487  }
488 
489  }
490 
492  {
493  if (mMenuDropMode)
494  {
495  mIsMenuDrop = false;
496  }
497  if ( ! _focus && mHideByLostKey)
498  {
499  setVisibleSmooth(false);
500  eventMenuCtrlClose(this);
501  }
503  }
504 
505  void MenuCtrl::notifyMouseSetFocus(Widget* _sender, Widget* _new)
506  {
508  }
509 
511  {
512  // заменяем
513  size_t index = getItemIndex(_item);
514  if (mItemsInfo[index].submenu != nullptr)
515  {
516  WidgetManager::getInstance().destroyWidget(mItemsInfo[index].submenu);
517  }
518  mItemsInfo[index].submenu = _widget;
519  // скрываем менюшку
520  mItemsInfo[index].submenu->setVisible(false);
521 
522  update();
523  }
524 
525  void MenuCtrl::_wrapItem(MenuItem* _item, size_t _index, const UString& _name, MenuItemType _type, const std::string& _id, Any _data)
526  {
527  _item->setAlign(mAlignVert ? Align::Top | Align::HStretch : Align::Default);
528  _item->setCoord(0, 0, _getClientWidget()->getWidth(), mHeightLine);
529  _item->eventRootKeyChangeFocus = newDelegate(this, &MenuCtrl::notifyRootKeyChangeFocus);
530  _item->eventMouseButtonClick = newDelegate(this, &MenuCtrl::notifyMouseButtonClick);
531  _item->eventMouseSetFocus = newDelegate(this, &MenuCtrl::notifyMouseSetFocus);
532 
533  setButtonImageIndex(_item, getIconIndexByType(_type ));
534 
535  MenuCtrl* submenu = nullptr;
536 
537  ItemInfo info = ItemInfo(_item, _name, _type, submenu, _id, _data);
538 
539  mItemsInfo.insert(mItemsInfo.begin() + _index, info);
540 
541  // его сет капшен, обновит размер
542  _item->setCaption(_name);
543 
544  update();
545  }
546 
547  void MenuCtrl::setVisible(bool _visible)
548  {
549 
550  if (mAnimateSmooth)
551  {
554  setEnabledSilent(true);
555  mAnimateSmooth = false;
556  }
557 
558  if (_visible)
559  {
560  if (mOwner == nullptr && mHideByLostKey)
561  {
563  }
564  }
565 
566  Base::setVisible(_visible);
567  }
568 
569  void MenuCtrl::setVisibleSmooth(bool _visible)
570  {
571  mAnimateSmooth = true;
573 
574  if (_visible)
575  {
576  setEnabledSilent(true);
577  if ( ! isVisible() )
578  {
580  Base::setVisible(true);
581  }
582 
583  ControllerFadeAlpha* controller = createControllerFadeAlpha(ALPHA_MAX, POPUP_MENU_SPEED_COEF, true);
585  ControllerManager::getInstance().addItem(this, controller);
586  }
587  else
588  {
589  setEnabledSilent(false);
590 
591  ControllerFadeAlpha* controller = createControllerFadeAlpha(ALPHA_MIN, POPUP_MENU_SPEED_COEF, false);
593  ControllerManager::getInstance().addItem(this, controller);
594  }
595  }
596 
597  ControllerFadeAlpha* MenuCtrl::createControllerFadeAlpha(float _alpha, float _coef, bool _enable)
598  {
600  ControllerFadeAlpha* controller = item->castType<ControllerFadeAlpha>();
601 
602  controller->setAlpha(_alpha);
603  controller->setCoef(_coef);
604  controller->setEnabled(_enable);
605 
606  return controller;
607  }
608 
609  MenuItem* MenuCtrl::insertItem(MenuItem* _to, const UString& _name, MenuItemType _type, const std::string& _id, Any _data)
610  {
611  return insertItemAt(getItemIndex(_to), _name, _type, _id, _data);
612  }
613 
614  MenuItem* MenuCtrl::addItem(const UString& _name, MenuItemType _type, const std::string& _id, Any _data)
615  {
616  return insertItemAt(ITEM_NONE, _name, _type, _id, _data);
617  }
618 
620  {
621  removeItemAt(getItemIndex(_item));
622  }
623 
625  {
626  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "MenuCtrl::getItemAt");
627  return mItemsInfo[_index].item;
628  }
629 
631  {
632  for (size_t pos=0; pos<mItemsInfo.size(); pos++)
633  {
634  if (mItemsInfo[pos].item == _item) return pos;
635  }
636  MYGUI_EXCEPT("item (" << _item << ") not found, source 'MenuCtrl::getItemIndex'");
637  }
638 
640  {
641  for (size_t pos=0; pos<mItemsInfo.size(); pos++)
642  {
643  if (mItemsInfo[pos].name == _name) return mItemsInfo[pos].item;
644  }
645  return nullptr;
646  }
647 
648  MenuItem* MenuCtrl::getItemById(const std::string& _id)
649  {
650  for (size_t pos=0; pos<mItemsInfo.size(); pos++)
651  {
652  if (mItemsInfo[pos].id == _id) return mItemsInfo[pos].item;
653  }
654  MYGUI_EXCEPT("item id (" << _id << ") not found, source 'MenuCtrl::getItemById'");
655  }
656 
657  size_t MenuCtrl::getItemIndexById(const std::string& _id)
658  {
659  for (size_t pos=0; pos<mItemsInfo.size(); pos++)
660  {
661  if (mItemsInfo[pos].id == _id) return pos;
662  }
663  MYGUI_EXCEPT("item id (" << _id << ") not found, source 'MenuCtrl::getItemById'");
664  }
665 
667  {
668  for (size_t pos=0; pos<mItemsInfo.size(); pos++)
669  {
670  if (mItemsInfo[pos].name == _name) return pos;
671  }
672  return ITEM_NONE;
673  }
674 
676  {
677  for (size_t pos=0; pos<mItemsInfo.size(); pos++)
678  {
679  if (mItemsInfo[pos].item == _item) return pos;
680  }
681  return ITEM_NONE;
682  }
683 
684  Widget* MenuCtrl::_getClientWidget()
685  {
686  return mWidgetClient == nullptr ? this : mWidgetClient;
687  }
688 
689  const Widget* MenuCtrl::_getClientWidget() const
690  {
691  return mWidgetClient == nullptr ? this : mWidgetClient;
692  }
693 
694 } // namespace MyGUI