1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "GUI/BsGUIMenu.h"
4#include "GUI/BsGUIDropDownMenu.h"
5
6namespace bs
7{
8 bool GUIMenuItemComparer::operator() (const GUIMenuItem* const& a, const GUIMenuItem* const& b) const
9 {
10 return a->mPriority > b->mPriority || (a->mPriority == b->mPriority && a->mSeqIdx < b->mSeqIdx);
11 }
12
13 GUIMenuItem::GUIMenuItem(GUIMenuItem* parent, const String& name, std::function<void()> callback,
14 INT32 priority, UINT32 seqIdx, const ShortcutKey& key)
15 :mParent(parent), mIsSeparator(false), mName(name), mCallback(callback), mPriority(priority),
16 mShortcut(key), mSeqIdx(seqIdx)
17 {
18
19 }
20
21 GUIMenuItem::GUIMenuItem(GUIMenuItem* parent, INT32 priority, UINT32 seqIdx)
22 : mParent(parent), mIsSeparator(true), mCallback(nullptr), mPriority(priority), mSeqIdx(seqIdx)
23 {
24
25 }
26
27 GUIMenuItem::~GUIMenuItem()
28 {
29 for(auto& child : mChildren)
30 bs_delete(child);
31 }
32
33 const GUIMenuItem* GUIMenuItem::findChild(const String& name) const
34 {
35 auto iterFind = std::find_if(begin(mChildren), end(mChildren), [&] (GUIMenuItem* x) { return x->getName() == name; });
36
37 if(iterFind != mChildren.end())
38 return *iterFind;
39
40 return nullptr;
41 }
42
43 GUIMenuItem* GUIMenuItem::findChild(const String& name)
44 {
45 auto iterFind = std::find_if(begin(mChildren), end(mChildren), [&] (GUIMenuItem* x) { return x->getName() == name; });
46
47 if(iterFind != mChildren.end())
48 return *iterFind;
49
50 return nullptr;
51 }
52
53 void GUIMenuItem::removeChild(const String& name)
54 {
55 auto iterFind = std::find_if(begin(mChildren), end(mChildren), [&] (GUIMenuItem* x) { return x->getName() == name; });
56
57 if(iterFind != mChildren.end())
58 {
59 bs_delete(*iterFind);
60 mChildren.erase(iterFind);
61 }
62 }
63
64 void GUIMenuItem::removeChild(const GUIMenuItem* item)
65 {
66 auto iterFind = std::find(begin(mChildren), end(mChildren), item);
67
68 if (iterFind != mChildren.end())
69 {
70 bs_delete(*iterFind);
71 mChildren.erase(iterFind);
72 }
73 }
74
75 GUIMenu::GUIMenu()
76 :mRootElement(nullptr, "", nullptr, 0, 0, ShortcutKey::NONE), mNextIdx(0)
77 {
78
79 }
80
81 GUIMenu::~GUIMenu()
82 {
83
84 }
85
86 GUIMenuItem* GUIMenu::addMenuItem(const String& path, std::function<void()> callback, INT32 priority, const ShortcutKey& key)
87 {
88 return addMenuItemInternal(path, callback, false, priority, key);
89 }
90
91 GUIMenuItem* GUIMenu::addSeparator(const String& path, INT32 priority)
92 {
93 return addMenuItemInternal(path, nullptr, true, priority, ShortcutKey::NONE);
94 }
95
96 GUIMenuItem* GUIMenu::addMenuItemInternal(const String& path, std::function<void()> callback, bool isSeparator,
97 INT32 priority, const ShortcutKey& key)
98 {
99 Vector<String> pathElements = StringUtil::split(path, "/");
100
101 GUIMenuItem* curSubMenu = &mRootElement;
102 for(UINT32 i = 0; i < (UINT32)pathElements.size(); i++)
103 {
104 if(pathElements[i] == "")
105 continue;
106
107 const String& pathElem = *(pathElements.begin() + i);
108 GUIMenuItem* existingItem = curSubMenu->findChild(pathElem);
109
110 if(existingItem == nullptr)
111 {
112 bool isLastElem = i == (UINT32)(pathElements.size() - 1);
113
114 if(isLastElem)
115 existingItem = bs_new<GUIMenuItem>(curSubMenu, pathElem, callback, priority, mNextIdx++, key);
116 else
117 {
118 existingItem = bs_alloc<GUIMenuItem>();
119 existingItem = new (existingItem) GUIMenuItem(curSubMenu, pathElem, nullptr, priority, mNextIdx++, ShortcutKey::NONE);
120 }
121
122 curSubMenu->addChild(existingItem);
123 }
124
125 curSubMenu = existingItem;
126 }
127
128 if(isSeparator)
129 {
130 GUIMenuItem* separatorItem = bs_new<GUIMenuItem>(curSubMenu, priority, mNextIdx++);
131 curSubMenu->addChild(separatorItem);
132
133 return separatorItem;
134 }
135
136 return curSubMenu;
137 }
138
139 GUIMenuItem* GUIMenu::getMenuItem(const String& path)
140 {
141 Vector<String> pathElements = StringUtil::split(path, "/");
142
143 GUIMenuItem* curSubMenu = &mRootElement;
144 for(UINT32 i = 0; i < (UINT32)pathElements.size(); i++)
145 {
146 const String& pathElem = *(pathElements.begin() + i);
147 GUIMenuItem* existingItem = curSubMenu->findChild(pathElem);
148
149 if(existingItem == nullptr || existingItem->isSeparator())
150 return nullptr;
151
152 curSubMenu = existingItem;
153 }
154
155 return curSubMenu;
156 }
157
158 void GUIMenu::removeMenuItem(const GUIMenuItem* item)
159 {
160 GUIMenuItem* parent = item->mParent;
161 assert(parent != nullptr);
162
163 parent->removeChild(item->getName());
164 }
165
166 GUIDropDownData GUIMenu::getDropDownData() const
167 {
168 return getDropDownDataInternal(mRootElement);
169 }
170
171 void GUIMenu::setLocalizedName(const String& menuItemLabel, const HString& localizedName)
172 {
173 mLocalizedEntryNames[menuItemLabel] = localizedName;
174 }
175
176 GUIDropDownData GUIMenu::getDropDownDataInternal(const GUIMenuItem& menu) const
177 {
178 GUIDropDownData dropDownData;
179
180 for(auto& menuItem : menu.mChildren)
181 {
182 if(menuItem->isSeparator())
183 {
184 dropDownData.entries.push_back(GUIDropDownDataEntry::separator());
185 }
186 else
187 {
188 if(menuItem->getNumChildren() == 0)
189 {
190 dropDownData.entries.push_back(GUIDropDownDataEntry::button(menuItem->getName(),
191 menuItem->getCallback(), menuItem->getShortcut().getName()));
192 }
193 else
194 {
195 dropDownData.entries.push_back(GUIDropDownDataEntry::subMenu(menuItem->getName(),
196 getDropDownDataInternal(*menuItem)));
197 }
198 }
199 }
200
201 dropDownData.localizedNames = mLocalizedEntryNames;
202
203 return dropDownData;
204 }
205}