YSTest: Menu.cpp 源文件

YSTest PreAlpha_b800_20170810 The YSLib Test Project

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

- 文件列表
- 文件成员

Menu.cpp
浏览该文件的文档.
1 /*
2 © 2011-2016 FrankHB.
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/YModules.h"
29 #include YFM_YSLib_UI_Menu
30 #include YFM_YSLib_UI_YWindow
31 #include YFM_YSLib_Service_YBrush
32 #include YFM_YSLib_Service_TextRenderer
33
34 namespace YSLib
35 {
36
37 using namespace Drawing;
38
39 namespace UI
40 {
41
42 Menu::Menu(const Rect& r, const shared_ptr<ListType>& h)
43 : TextList(r, h, FetchGUIConfiguration().Colors.GetPair(Styles::Highlight,
44 Styles::HighlightText)),
45 vDisabled(h ? h->size() : 0)
46 {
47 auto& unit(GetUnitRef());
48
49 yunseq(
50 Background = SolidBrush(FetchGUIConfiguration().Colors[Styles::Panel]),
51 LabelBrush.Margin = Padding(6, 18, 4, 4),
52 CyclicTraverse = true,
53 SelectionOptions[ClearSelectionOnConfirm] = true,
54 SelectionOptions[ClearSelectionOnLeave] = true,
55 SelectionOptions[SelectOnHover] = true,
56 IsRelated = [this](IWidget& wgt){
57 if(pHost)
58 {
59 const auto idx(GetSelectedIndex());
60
61 if(idx != size_t(-1))
62 {
63 const auto i(mSubMenus.find(idx));
64
65 if(i != mSubMenus.end() && i->second
66 && pHost->IsShowing(*i->second))
67 return true;
68 }
69 }
70
71 auto p_mnu = dynamic_cast< Menu*>(&wgt);
72
73 if(!p_mnu)
74 p_mnu = dynamic_cast< Menu*>(FetchContainerPtr(wgt).get());
75 return p_mnu ? p_mnu->GetParentPtr().get() == this : false;
76 },
77 FetchEvent<KeyDown>(*this) += [this](KeyEventArgs&& e){
78 if(pHost)
79 {
80 const auto& k(e.GetKeys());
81
82 if(k.count() == 1)
83 {
84 if(k[KeyCodes::Right])
85 {
86 if(IsSelected())
87 TryShowingSub(GetSelectedIndex());
88 }
89 else if(k[KeyCodes::Left] || k[KeyCodes::Esc])
90 {
91 if(const auto p_mnu = GetParentPtr())
92 RequestFocus(*p_mnu);
93 else if(k[KeyCodes::Esc])
94 Hide();
95 }
96 }
97 }
98 },
99 FetchEvent<CursorOver>(*this) += [this](CursorEventArgs&& e){
100 if(SelectionOptions[SelectOnHover]
101 && e.Strategy != RoutedEventArgs::Bubble
102 && FetchGUIState().IsCursorMoved())
103 {
104 const auto idx(CheckPoint(GetSizeOf(*this), e));
105
106 if(idx != size_t(-1))
107 {
108 RequestFocus(*this);
109 TryShowingSub(idx);
110 }
111 }
112 },
113 FetchEvent<LostFocus>(*this) += [this](UIEventArgs&& e){
114 if(pHost)
115 {
116 {
117 const auto i(pHost->Roots.find(e.GetSender()));
118
119 if(i != pHost->Roots.end())
120 {
121 auto p_mnu(make_observer(this));
122
123 while(const auto p = p_mnu->GetParentPtr())
124 p_mnu = p;
125 if(i->second == p_mnu)
126 return;
127 }
128 }
129 if(const auto p_mnu = dynamic_cast<Menu*>(&e.GetSender()))
130 {
131 if(p_mnu->GetParentPtr().get() != this)
132 pHost->HideUnrelated(*this, *p_mnu);
133 }
134 else
135 pHost->HideAll();
136 }
137 },
138 Confirmed += [this](IndexEventArgs&& e){
139 if(Contains(e) && pHost && !ShowSub(e.Value))
140 pHost->HideAll();
141 },
142 Iterated += [this](size_t idx){
143 SetEnabledOf(GetUnitRef(), IsItemEnabled(idx));
144 },
145 FetchEvent<Paint>(unit).Add([this]{
146 // TODO: Handle different highlight text colors.
147 if(!IsEnabled(GetUnitRef()))
148 LabelBrush.ForeColor
149 = FetchGUIConfiguration().Colors[Styles::GrayText];
150 }, BackgroundPriority),
151 FetchEvent<Paint>(unit) += [this](PaintEventArgs&& e){
152 const auto& ubound(GetBoundsOf(GetUnitRef()) + e.Location);
153
154 if(YB_LIKELY(ubound.Width > 16) && ystdex::exists(mSubMenus, idxShared))
155 DrawArrow(e.Target, e.ClipArea, Rect(ubound.GetRight() - 16,
156 ubound.Y, 16, ubound.Height), 4, RDeg0, LabelBrush.ForeColor);
157 }
158 );
159 }
160 ImplDeDtor(Menu)
161
162 void
163 Menu::operator+=(const ValueType& val)
164 {
165 if(val.second && IsInInterval(val.first, GetList().size()))
166 {
167 val.second->pParent = make_observer(this);
168 mSubMenus.insert(val);
169 }
170 }
171
172 bool
173 Menu::operator-=(IndexType idx)
174 {
175 if(IsInInterval(idx, GetList().size()))
176 {
177 const auto i(mSubMenus.find(idx));
178
179 if(i != mSubMenus.end() && i->second)
180 {
181 i->second->pParent = {};
182 mSubMenus.erase(i);
183 return true;
184 }
185 }
186 return {};
187 }
188
189 bool
190 Menu::IsItemEnabled(ListType::size_type idx) const
191 {
192 YAssert(IsInInterval(idx, GetList().size()), "Index is out of range.");
193 AdjustSize();
194 return !vDisabled[idx];
195 }
196
197 void
198 Menu::SetItemEnabled(Menu::ListType::size_type idx, bool b)
199 {
200 YAssert(IsInInterval(idx, GetList().size()), "Index is out of range.");
201 AdjustSize();
202 vDisabled[idx] = !b;
203 }
204
205 void
206 Menu::AdjustSize() const
207 {
208 const auto list_size(GetList().size());
209
210 if(vDisabled.size() != list_size)
211 vDisabled.resize(list_size);
212 }
213
214 bool
215 Menu::Hide()
216 {
217 if(pHost)
218 {
219 pHost->Hide(*this);
220 return true;
221 }
222 return {};
223 }
224
225 bool
226 Menu::Show()
227 {
228 if(pHost)
229 {
230 pHost->Show(*this);
231 return true;
232 }
233 return {};
234 }
235
236 observer_ptr<Menu>
237 Menu::ShowSub(IndexType idx)
238 {
239 if(pHost)
240 {
241 const auto i(mSubMenus.find(idx));
242
243 if(i != mSubMenus.end())
244 {
245 auto& mnu(*i->second);
246
247 LocateMenu(mnu, *this, idx);
248 mnu.Show();
249 return make_observer(&mnu);
250 }
251 }
252 return {};
253 }
254
255 void
256 Menu::TryShowingSub(IndexType idx)
257 {
258 if(const auto p_mnu = ShowSub(idx))
259 p_mnu->SelectFirst();
260 }
261
262 void
263 LocateMenu(Menu& dst, const Menu& src, Menu::IndexType idx)
264 {
265 const Rect& r(GetBoundsOf(src));
266
267 // XXX: Conversion to 'SPos' might be implementation-defined.
268 SetLocationOf(dst, {r.GetRight(), r.Y + SPos(src.GetItemHeight() * idx)});
269 }
270
271
272 void
273 MenuHost::operator+=(Menu& mnu)
274 {
275 menus.insert(mnu);
276 mnu.pHost = make_observer(this);
277 UI::Hide(mnu);
278 }
279
280 bool
281 MenuHost::operator-=(Menu& mnu)
282 {
283 if(menus.erase(mnu) != 0)
284 {
285 mnu.pHost = {};
286 return true;
287 }
288 return {};
289 }
290
291 bool
292 MenuHost::IsShowing(Menu& mnu) const
293 {
294 return Contains(mnu) ? IsVisible(mnu) : false;
295 }
296
297 void
298 MenuHost::Clear()
299 {
300 menus.clear();
301 }
302
303 void
304 MenuHost::Show(Menu& mnu)
305 {
306 if(Contains(mnu))
307 ShowRaw(mnu);
308 }
309
310 void
311 MenuHost::ShowAll()
312 {
313 std::for_each(menus.begin(), menus.end(), [this](IWidget& wgt){
314 ShowRaw(ystdex::polymorphic_downcast<Menu&>(wgt));
315 });
316 }
317
318 void
319 MenuHost::ShowRaw(Menu& mnu)
320 {
321 YAssert(Contains(mnu), "Invalid menu found.");
322
323 UI::Show(mnu);
324 }
325
326 void
327 MenuHost::Hide(Menu& mnu)
328 {
329 if(Contains(mnu))
330 HideRaw(mnu);
331 }
332
333 void
334 MenuHost::HideAll()
335 {
336 std::for_each(menus.begin(), menus.end(), [this](Menu& mnu){
337 HideRaw(mnu);
338 });
339 }
340
341 void
342 MenuHost::HideRaw(Menu& mnu)
343 {
344 YAssert(Contains(mnu), "Invalid menu found.");
345
346 ReleaseFocus(mnu);
347 if(IsVisible(mnu))
348 {
349 Invalidate(mnu);
350 UI::Hide(mnu);
351 }
352 }
353
354 void
355 MenuHost::HideUnrelated(Menu& mnu, Menu& mnu_parent)
356 {
357 if(Contains(mnu_parent))
358 {
359 auto p_mnu(make_observer(&mnu));
360
361 for(; p_mnu && p_mnu != make_observer(&mnu_parent);
362 p_mnu = p_mnu->GetParentPtr())
363 // XXX: %Hide or %HideRaw?
364 Hide(*p_mnu);
365 if(!p_mnu)
366 HideAll();
367 }
368 else
369 HideAll();
370 }
371
372
373 void
374 BindTopLevelPopupMenu(MenuHost& mh, Menu& mnu, IWidget& wgt, KeyIndex k)
375 {
376 FetchEvent<Click>(wgt) += [&, k](CursorEventArgs&& e){
377 if(e.Strategy == RoutedEventArgs::Direct && e.Keys.count() == 1 && e[k])
378 {
379 mnu.GetView().SetLocation(LocateForTopOffset(e.Position, wgt));
380 mh.Show(mnu);
381 }
382 };
383 }
384
385 } // namespace UI;
386
387 } // namespace YSLib;
388
YSLib::UI::LocateMenu
void LocateMenu(Menu &dst, const Menu &src, Menu::IndexType idx)
Definition: Menu.cpp:263
YSLib::UI::BindTopLevelPopupMenu
YF_API void BindTopLevelPopupMenu(MenuHost &, Menu &, IWidget &, KeyIndex k=KeyCodes::Secondary)
绑定顶层部件的按键事件弹出菜单。
Definition: Menu.cpp:374
YSLib::UI::TextList::SelectOnHover
悬停时自动选择。
Definition: ListControl.h:214
YSLib::UI::Menu::Menu
Menu(const Rect &={}, const shared_ptr< ListType > &={})
构造：使用指定边界、文本列表和菜单标识。
Definition: Menu.cpp:42
YSLib::UI::GetBoundsOf
pt pt Y const IWidget &wgt const IWidget &wgt const IWidget &wgt GetBoundsOf
Definition: YWidget.h:168
YSLib::UI::LocateForTopOffset
Point LocateForTopOffset(const Point &origin, IWidget &wgt, const Point &pt)
Definition: YUIContainer.cpp:91
YSLib::UI::MenuHost::IsShowing
bool IsShowing(Menu &) const
判断窗口中是否正在显示指定的菜单。
Definition: Menu.cpp:292
NPL::dst
YF_API const char src const string const char * dst
Definition: Dependency.h:95
YSLib::UI::FetchGUIConfiguration
GUIConfiguration & FetchGUIConfiguration()
Definition: YGUI.cpp:519
YSLib::UI::RoutedEventArgs::Bubble
气泡事件：向上遍历视图树时触发。
Definition: YWidgetEvent.h:107
YSLib::UI::GValueEventArgs
简单 UI 事件参数类。
Definition: YWidgetEvent.h:299
YSLib::Drawing::GBinaryGroup::Y
_type Y
分量。
Definition: YGDIBase.h:63
YSLib::UI::Menu::AdjustSize
void AdjustSize() const
设置 idx 指定的菜单项的可用性。
Definition: Menu.cpp:206
YSLib
YSLib 命名空间。
Definition: AndroidHost.h:45
UI
YSLib::UI::CursorEventArgs
指针设备输入事件参数类。
Definition: YWidgetEvent.h:210
ystdex::make_observer
observer_ptr< _type > make_observer(_type *p)
Definition: pointer.hpp:326
YSLib::Drawing::r
The boundary is out of the const Graphics const Rect const Point const Point Color c const Rect const Rect & r
Definition: YDraw.h:174
YSLib::UI::AMUnitControlList::GetUnitRef
IWidget & GetUnitRef() const ImplI(AMUnitList)
Definition: ListControl.cpp:50
YSLib::Drawing::DrawArrow
YF_API void DrawArrow(const Graphics &, const Rect &, const Rect &, SDst=4, Rotation=RDeg0, Color=ColorSpace::Black, bool={})
在指定图形接口上下文上描画底边和坐标轴共线的等腰直角三角形箭头。
Definition: YStyle.cpp:202
YSLib::UI::MLabel::ForeColor
Color ForeColor
默认前景色。
Definition: Label.h:77
YSLib::UI::MenuHost::ShowRaw
void ShowRaw(Menu &mnu)
显示指定菜单 mnu 。
Definition: Menu.cpp:319
YSLib::UI::Styles::HighlightText
Definition: YStyle.h:327
YSLib::UI::wgt
IWidget & wgt
Definition: YControl.h:112
YSLib::UI::RoutedEventArgs::Direct
直接事件：仅当遍历至目标控件时触发。
Definition: YWidgetEvent.h:109
YSLib::UI::Menu::pHost
observer_ptr< MenuHost > pHost
宿主指针。
Definition: Menu.h:71
YSLib::UI::TextList::IsRelated
std::function< bool(IWidget &)> IsRelated
判断是否为 Leave 目标时不取消选择的相关部件。
Definition: ListControl.h:221
YSLib::UI::MLabel::Margin
Drawing::Padding Margin
文本和容器的间距。
Definition: Label.h:79
YSLib::UI::Menu::IsItemEnabled
bool IsItemEnabled(ListType::size_type) const
判断菜单项是否有效。
Definition: Menu.cpp:190
YSLib::UI::TextList
文本列表。
Definition: ListControl.h:201
YSLib::YAssert
YAssert(!(v< Deref(a)),"Value less than lower bound found.")
yunseq
#define yunseq
无序列依赖表达式组求值。
Definition: ydef.h:1022
YSLib::UI::IsVisible
IsVisible
Definition: YWidget.h:101
YSLib::UI::Menu::TryShowingSub
void TryShowingSub(IndexType)
尝试显示索引指定的子菜单并选择子菜单的首个菜单项。
Definition: Menu.cpp:256
YSLib::UI::Invalidate
YF_API void Invalidate(IWidget &, const Rect &)
无效化：使相对部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: YWidget.cpp:108
YSLib::IsInInterval
YB_STATELESS bool IsInInterval(_type i, _type b) ynothrow
判断 i 是否在左闭右开区间 [_type(0), b) 中。
Definition: YCoreUtilities.h:84
YSLib::UI::FetchGUIState
GUIState & FetchGUIState() ynothrow
Definition: YGUI.cpp:508
YSLib::UI::MenuHost::operator-=
bool operator-=(Menu &)
从菜单组移除指定的菜单。
Definition: Menu.cpp:281
YSLib::UI::BackgroundPriority
yconstexpr const EventPriority BackgroundPriority(0xC0)
用户界面绘制优先级。
YSLib::UI::AMUnitControlList::Iterated
GEvent< void(size_t)> Iterated
迭代器迭代事件。
Definition: ListControl.h:90
YSLib::UI::AMUnitList::GetItemHeight
SDst GetItemHeight() const
取项目行高：调用 GetItemHeightCore 取结果。
Definition: Viewer.cpp:167
platform::KeyCodes::DS::Esc
static yconstexpr const NativeSet Esc
Definition: Keys.h:237
YSLib::UI::MenuHost::HideRaw
void HideRaw(Menu &mnu)
隐藏指定菜单 mnu。
Definition: Menu.cpp:342
YSLib::UI::MTextList::LabelBrush
MLabel LabelBrush
Definition: ListControl.h:150
YSLib::UI::Menu::mSubMenus
SubMap mSubMenus
子菜单映射表：存储非空子菜单指针。
Definition: Menu.h:76
YSLib::UI::SetLocationOf
YF_API void SetLocationOf(IWidget &, const Point &)
设置部件左上角所在位置（相对容器的偏移坐标）。
Definition: YWidget.cpp:76
ystdex::exists
bool exists(const _tCon &con, const _tKey &key)
判断指定的容器中存在指定的键。
Definition: container.hpp:681
YSLib::UI::MenuHost::ShowAll
void ShowAll()
显示菜单组中的所有菜单。
Definition: Menu.cpp:311
YSLib::i
return i
Definition: YCoreUtilities.h:176
YSLib::UI::MenuHost::HideAll
void HideAll()
隐藏菜单组中的所有菜单。
Definition: Menu.cpp:334
YSLib::UI::TextList::SelectionOptions
std::bitset< SelectionOptionMax > SelectionOptions
选择操作选项。
Definition: ListControl.h:219
Drawing
YSLib::UI::PaintEventArgs
部件绘制参数。
Definition: YWidgetEvent.h:323
YSLib::UI::AMUnitList::CheckPoint
size_t CheckPoint(const Size &, const Point &)
检查点（相对左上角）是否在选择范围内，
Definition: Viewer.cpp:263
YSLib::UI::MenuHost::HideUnrelated
void HideUnrelated(Menu &mnu, Menu &mnuParent)
隐藏从 mnu 起向上层遍历菜单树的过程中不相关的菜单。
Definition: Menu.cpp:355
platform::KeyIndex
size_t KeyIndex
按键索引类型：标识特定的按键在按键集合中的位置。
Definition: Keys.h:42
YSLib::UI::e
void e
Definition: YWidget.h:352
YSLib::UI::TextList::ClearSelectionOnLeave
离开时自动清空选择。
Definition: ListControl.h:212
YSLib::UI::Menu::operator-=
bool operator-=(IndexType)
向菜单组移除指定子菜单索引项。
Definition: Menu.cpp:173
YSLib::UI::MTextList::IndexType
typename ListType::size_type IndexType
索引类型。
Definition: ListControl.h:143
YModules.h
YSLib::UI::KeyEventArgs
按键输入事件参数类。
Definition: YWidgetEvent.h:186
YSLib::UI::Show
YF_API void Show(IWidget &)
显示部件。
Definition: YWidget.cpp:202
YSLib::UI::Menu::pParent
observer_ptr< Menu > pParent
父菜单指针。
Definition: Menu.h:73
YSLib::Drawing::Rect
屏幕标准矩形：表示屏幕矩形区域。
Definition: YGDIBase.h:540
YSLib::UI::SetEnabledOf
SetEnabledOf
Definition: YControl.h:112
YSLib::UI::Menu::Hide
bool Hide()
隐藏菜单。
Definition: Menu.cpp:215
YSLib::Drawing::Padding
空白样式。
Definition: YGDI.h:46
YSLib::UI::MenuHost::menus
set< lref< Menu >, ystdex::get_less< Menu > > menus
Definition: Menu.h:201
YSLib::UI::AMUnitList::idxShared
size_t idxShared
Definition: Viewer.h:203
YSLib::UI::Widget::Background
HBrush Background
背景。
Definition: YWidget.h:391
YSLib::UI::Menu::Show
bool Show()
显示菜单。
Definition: Menu.cpp:226
YSLib::UI::TextList::ClearSelectionOnConfirm
确认时自动清空选择。
Definition: ListControl.h:210
YSLib::UI::Menu
菜单。
Definition: Menu.h:57
YSLib::UI::Hide
YF_API void Hide(IWidget &)
隐藏部件。
Definition: YWidget.cpp:100
YSLib::UI::Widget::GetView
GetView()) DefGetter(const ImplI(IWidget)
YSLib::Drawing::SolidBrush
单色画刷。
Definition: YBrush.h:58
YSLib::Drawing::RDeg0
Definition: YGDIBase.h:897
YSLib::UI::BorderArea::Right
YSLib::UI::UIEventArgs
用户界面事件参数基类。
Definition: YWidgetEvent.h:59
YSLib::UI::SPos
const IWidget &wgt SPos
Definition: YWidget.h:109
YSLib::UI::Styles::GrayText
Definition: YStyle.h:330
YSLib::UI::Menu::LocateMenu
YF_API void LocateMenu(Menu &, const Menu &, Menu::IndexType)
定位菜单：以第二个参数作为参考父菜单，按指定参考偏移索引定位菜单。
Definition: Menu.cpp:263
YSLib::UI::Styles::Panel
面板背景。
Definition: YStyle.h:312
YSLib::UI::MenuHost::Show
PDefH(bool, Contains, Menu &mnu) const ImplRet(ystdex void Show(Menu &)
判断菜单组中是否存在指定的菜单。
Definition: Menu.h:257
YSLib::UI::MenuHost
菜单宿主。
Definition: Menu.h:197
YSLib::UI::AMUnitControlList::Confirmed
GEvent< void(IndexEventArgs)> Confirmed
项目选中确认事件。
Definition: ListControl.h:85
ystdex::size
auto size(const _tRange &c) -> decltype(c.size())
类容器访问。
Definition: range.hpp:233
YB_LIKELY
#define YB_LIKELY(_expr)
Definition: ydef.h:463
YSLib::UI::MenuHost::Hide
void Hide(Menu &)
隐藏菜单组中指定的菜单。
Definition: Menu.cpp:327
YSLib::UI::Menu::vDisabled
vector< bool > vDisabled
未启用菜单项。
Definition: Menu.h:78
YSLib::UI::GetSizeOf
pt pt Y const IWidget &wgt const IWidget &wgt GetSizeOf
无效化：使相对部件的子部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: YWidget.h:161
YSLib::UI::BorderArea::Left
YSLib::UI::Styles::Highlight
高亮背景。
Definition: YStyle.h:319
YSLib::UI::Menu::ShowSub
observer_ptr< Menu > ShowSub(IndexType)
显示索引指定的子菜单。
Definition: Menu.cpp:237
CHRLib::p
const auto p(d)
YSLib::UI::ImplDeDtor
ImplDeDtor(Thumb) void DecorateAsCloseButton(Thumb &tmb)
Definition: Button.cpp:125