YSTest: menu.cpp 源文件

YSTest PreAlpha_b400_20130424 The YSLib Test Project

- 首页
- 相关页面
- 模块
- 命名空间
- 类
- 文件

- 文件列表
- 文件成员

全部 类 命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义 组 页
menu.cpp
浏览该文件的文档.
1 ﻿/*
2 Copyright by FrankHB 2011 - 2013.
3
4 This file is part of the YSLib project, and may only be used,
5 modified, and distributed under the terms of the YSLib project
6 license, LICENSE.TXT. By continuing to use, modify, or distribute
7 this file you indicate that you have read the license and
8 understand and accept it fully.
9 */
10
28 #include "YSLib/UI/menu.h"
29 #include "YSLib/UI/ywindow.h"
30 #include "YSLib/UI/YBrush.h"
31 #include "YSLib/Service/TextRenderer.h"
32
33 YSL_BEGIN
34
35 using namespace Drawing;
36
37 YSL_BEGIN_NAMESPACE(UI)
38
39 Menu::Menu(const Rect& r, const shared_ptr<ListType>& h, ID id)
40 : TextList(r, h, FetchGUIState().Colors.GetPair(Styles::Highlight,
41 Styles::HighlightText)),
42 id(id), pParent(nullptr), mSubMenus(), vDisabled(h ? h->size() : 0)
43 {
44 Background = SolidBrush(FetchGUIState().Colors[Styles::Panel]),
45 Margin = Padding(6, 18, 4, 4);
46 CyclicTraverse = true;
47 yunseq(
48 FetchEvent<KeyDown>(*this) += [this](KeyEventArgs&& e){
49 if(pHost)
50 {
51 const auto& k(e.GetKeys());
52
53 if(k.count() == 1)
54 {
55 if(k[KeyCodes::Right])
56 {
57 if(IsSelected())
58 if(const auto pMnu = ShowSub(GetSelectedIndex()))
59 pMnu->SelectFirst();
60 }
61 else if(k[KeyCodes::Left] || k[KeyCodes::Esc])
62 {
63 if(const auto pMnu = GetParentPtr())
64 RequestFocus(*pMnu);
65 else if(k[KeyCodes::Esc])
66 Hide();
67 }
68 }
69 }
70 },
71 FetchEvent<LostFocus>(*this) += [this](UIEventArgs&& e){
72 if(pHost)
73 {
74 {
75 const auto i(pHost->Roots.find(&e.GetSender()));
76
77 if(i != pHost->Roots.end())
78 {
79 auto pMnu(this);
80
81 while(const auto pParent = pMnu->GetParentPtr())
82 pMnu = pParent;
83 if(i->second == pMnu->id)
84 return;
85 }
86 }
87 if(const auto pMnu = dynamic_cast<Menu*>(&e.GetSender()))
88 {
89 if(pMnu->GetParentPtr() != this)
90 pHost->HideUnrelated(*this, *pMnu);
91 }
92 else
93 pHost->HideAll();
94 }
95 },
96 GetConfirmed() += [this](IndexEventArgs&& e){
97 if(Contains(e) && pHost && !ShowSub(e.Value))
98 pHost->HideAll();
99 }
100 );
101 //刷新文本状态，防止第一次绘制前不确定文本间距，无法正确根据内容重设大小。
102 RefreshTextState();
103 }
104
105 void
106 Menu::operator+=(const ValueType& val)
107 {
108 if(val.second && IsInInterval(val.first, GetList().size()))
109 {
110 val.second->pParent = this;
111 mSubMenus.insert(val);
112 }
113 }
114
115 bool
116 Menu::operator-=(IndexType idx)
117 {
118 if(IsInInterval(idx, GetList().size()))
119 {
120 const auto i(mSubMenus.find(idx));
121
122 if(i != mSubMenus.end() && i->second)
123 {
124 i->second->pParent = nullptr;
125 mSubMenus.erase(i);
126 return true;
127 }
128 return false;
129 }
130 return false;
131 }
132
133 bool
134 Menu::IsItemEnabled(ListType::size_type idx) const
135 {
136 YAssert(IsInInterval(idx, GetList().size()), "Index is out of range.");
137
138 AdjustSize();
139 return !vDisabled[idx];
140 }
141
142 void
143 Menu::SetItemEnabled(Menu::ListType::size_type idx, bool b)
144 {
145 YAssert(IsInInterval(idx, GetList().size()), "Index is out of range.");
146
147 AdjustSize();
148 vDisabled[idx] = !b;
149 }
150
151 void
152 Menu::AdjustSize() const
153 {
154 const auto list_size(GetList().size());
155
156 if(vDisabled.size() != list_size)
157 vDisabled.resize(list_size);
158 }
159
160 bool
161 Menu::CheckConfirmed(Menu::ListType::size_type idx) const
162 {
163 return TextList::CheckConfirmed(idx) && IsItemEnabled(idx);
164 }
165
166 bool
167 Menu::Show(ZOrderType z)
168 {
169 if(pHost)
170 {
171 pHost->Show(id, z);
172 return true;
173 }
174 return false;
175 }
176
177 Menu*
178 Menu::ShowSub(IndexType idx, ZOrderType z)
179 {
180 if(pHost)
181 {
182 const auto i(mSubMenus.find(idx));
183
184 if(i != mSubMenus.end())
185 {
186 auto& mnu(*i->second);
187
188 LocateMenu(mnu, *this, idx);
189 mnu.Show(z);
190 return &mnu;
191 }
192 }
193 return nullptr;
194 }
195
196 bool
197 Menu::Hide()
198 {
199 if(pHost)
200 {
201 pHost->Hide(id);
202 return true;
203 }
204 return false;
205 }
206
207 void
208 Menu::DrawItem(const Graphics& g, const Rect& mask, const Rect& unit,
209 ListType::size_type i)
210 {
211 Color t(tsList.Color);
212
213 // TODO: Handle different highlight text colors.
214
215 if(!IsItemEnabled(i))
216 tsList.Color = FetchGUIState().Colors[Styles::GrayText];
217 TextList::DrawItem(g, mask, unit, i);
218 tsList.Color = t;
219 if(YB_LIKELY(unit.Width > 16))
220 if(mSubMenus.find(i) != mSubMenus.end())
221 DrawArrow(g, Rect(unit.X + unit.Width - 16, unit.Y, 16,
222 unit.Height), 4, RDeg0, ForeColor);
223 }
224
225
226 void
227 LocateMenu(Menu& dst, const Menu& src, Menu::IndexType idx)
228 {
229 SetLocationOf(dst, Point(src.GetX() + src.GetWidth(),
230 src.GetY() + src.GetItemHeight() * idx));
231 }
232
233
234 MenuHost::MenuHost(Window& frm)
235 : Frame(frm), mMenus(), Roots()
236 {}
237 MenuHost::~MenuHost()
238 {
239 HideAll();
240 Clear();
241 }
242
243 void
244 MenuHost::operator+=(const ValueType& val)
245 {
246 YAssert(val.second, "Null pointer found.");
247
248 yunseq(mMenus[val.first] = val.second,
249 val.second->id = val.first, val.second->pHost = this);
250 }
251
252 void
253 MenuHost::operator+=(Menu& mnu)
254 {
255 mMenus[mnu.id] = &mnu;
256 mnu.pHost = this;
257 }
258
259 bool
260 MenuHost::operator-=(Menu::ID id)
261 {
262 const auto i(mMenus.find(id));
263
264 if(i != mMenus.end())
265 {
266 auto& mnu(*i->second);
267
268 mnu.pHost = nullptr;
269 mMenus.erase(i);
270 return true;
271 }
272 return false;
273 }
274
275 bool
276 MenuHost::IsShowing(Menu::ID id)
277 {
278 const auto i(mMenus.find(id));
279
280 return i == mMenus.end() ? false : Frame.Contains(*i->second);
281 }
282
283 bool
284 MenuHost::Contains(Menu& mnu)
285 {
286 using ystdex::get_value;
287
288 return std::find(mMenus.begin() | get_value, mMenus.end() | get_value, &mnu)
289 != mMenus.end();
290 }
291
292 void
293 MenuHost::Clear()
294 {
295 std::for_each(mMenus.begin(), mMenus.end(), delete_second_mem());
296 mMenus.clear();
297 }
298
299 void
300 MenuHost::Show(Menu::ID id, ZOrderType z)
301 {
302 const auto i(mMenus.find(id));
303
304 if(i != mMenus.end())
305 ShowRaw(*i->second, z);
306 }
307
308 void
309 MenuHost::ShowAll(ZOrderType z)
310 {
311 using ystdex::get_value;
312
313 std::for_each(mMenus.cbegin() | get_value, mMenus.cend() | get_value,
314 [this, z](const ItemType& pMnu){
315 if(pMnu)
316 ShowRaw(*pMnu, z);
317 });
318 }
319
320 void
321 MenuHost::ShowRaw(Menu& mnu, ZOrderType z)
322 {
323 Frame.Add(mnu, z);
324 //依赖于 mnu 的 GotFocus 事件默认会调用自身的 Invalidate 函数。
325 // Invalidate(mnu);
326 RequestFocus(mnu);
327 }
328
329 void
330 MenuHost::Hide(Menu::ID id)
331 {
332 const auto i(mMenus.find(id));
333
334 if(i != mMenus.end())
335 HideRaw(*i->second);
336 }
337
338 void
339 MenuHost::HideAll()
340 {
341 using ystdex::get_value;
342
343 std::for_each(mMenus.cbegin() | get_value, mMenus.cend() | get_value,
344 [this](const ItemType& pMnu){
345 if(pMnu)
346 HideRaw(*pMnu);
347 });
348 }
349
350 void
351 MenuHost::HideRaw(Menu& mnu)
352 {
353 ReleaseFocus(mnu);
354 if(IsVisible(mnu))
355 Invalidate(mnu);
356 Frame -= mnu;
357 }
358
359 void
360 MenuHost::HideUnrelated(Menu& mnu, Menu& mnuParent)
361 {
362 if(Contains(mnuParent))
363 {
364 auto pMnu(&mnu);
365
366 while(pMnu && pMnu != &mnuParent)
367 {
368 const auto i(mMenus.find(pMnu->GetID()));
369
370 if(i != mMenus.end())
371 HideRaw(*i->second);
372 pMnu = pMnu->GetParentPtr();
373 }
374 if(!pMnu)
375 HideAll();
376 }
377 else
378 HideAll();
379 }
380
381 YSL_END_NAMESPACE(UI)
382
383 YSL_END
384