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"
32 
34 
35 using namespace Drawing;
36 
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
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
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 {}
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
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
277 {
278  const auto i(mMenus.find(id));
279 
280  return i == mMenus.end() ? false : Frame.Contains(*i->second);
281 }
282 
283 bool
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
294 {
295  std::for_each(mMenus.begin(), mMenus.end(), delete_second_mem());
296  mMenus.clear();
297 }
298 
299 void
301 {
302  const auto i(mMenus.find(id));
303 
304  if(i != mMenus.end())
305  ShowRaw(*i->second, z);
306 }
307 
308 void
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
322 {
323  Frame.Add(mnu, z);
324 //依赖于 mnu 的 GotFocus 事件默认会调用自身的 Invalidate 函数。
325 // Invalidate(mnu);
326  RequestFocus(mnu);
327 }
328 
329 void
331 {
332  const auto i(mMenus.find(id));
333 
334  if(i != mMenus.end())
335  HideRaw(*i->second);
336 }
337 
338 void
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
352 {
353  ReleaseFocus(mnu);
354  if(IsVisible(mnu))
355  Invalidate(mnu);
356  Frame -= mnu;
357 }
358 
359 void
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 
382 
383 YSL_END
384