YSTest: YGUI.cpp 源文件

YSTest PreAlpha_b700_20160611 The YSLib Test Project

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

- 文件列表
- 文件成员

YGUI.cpp
浏览该文件的文档.
1 /*
2 © 2009-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_YGUI
30 #include YFM_YSLib_UI_YDesktop
31 #include <ystdex/cast.hpp> // for ystdex::polymorphic_downcast;
32
33 namespace YSLib
34 {
35
36 using namespace Drawing;
37
38 namespace UI
39 {
40
41 namespace
42 {
43
45 IWidget&
46 FetchTopEnabledAndVisibleWidget(IWidget& con, const Point& pt,
47 VisualEvent id)
48 {
49 for(auto pr(con.GetChildren()); pr.first != pr.second; ++pr.first)
50 {
51 auto& wgt(*pr.first);
52
53 if(Contains(wgt, pt) && IsVisible(wgt)
54 && wgt.GetController().IsEventEnabled(id))
55 return wgt;
56 }
57 return con;
58 }
59
61 inline PDefH(bool, IsVisibleEnabled, IWidget& wgt)
62 ImplRet(IsVisible(wgt) && IsEnabled(wgt))
63
64 } // unnamed namespace;
65
66
67 yconstexpr const InputTimer::Duration InputTimer::DefaultDuration;
68
69 InputTimer::InputTimer(Duration d) ynothrow
70 : Timer(d)
71 {}
72
73 bool
74 InputTimer::RefreshHeld(HeldStateType& s, Duration initial_delay,
75 Duration repeated_delay)
76 {
77 switch(s)
78 {
79 case Free:
80 yunseq(s = Pressed, Interval = initial_delay);
81 Activate(*this);
82 break;
83 case Pressed:
84 case Held:
85 if(YB_UNLIKELY(CheckTimeout(*this)))
86 {
87 if(s == Pressed)
88 yunseq(s = Held, Interval = repeated_delay);
89 return true;
90 }
91 break;
92 }
93 return {};
94 }
95 ImplDeDtor(InputTimer)
96
97 size_t
98 InputTimer::RefreshTap(size_t s, Duration delay)
99 {
100 const auto res(s == 0 || YB_UNLIKELY(!CheckTimeout(*this)) ? s + 1 : 0);
101
102 Interval = delay;
103 Activate(*this);
104 return res;
105 }
106
107
108 bool
109 RepeatHeld(InputTimer& tmr, InputTimer::HeldStateType& st,
110 const Timers::Duration& initial_delay,
111 const Timers::Duration& repeated_delay)
112 {
113 const bool b(st == InputTimer::Free);
114
115 return tmr.RefreshHeld(st, initial_delay, repeated_delay) || b;
116 }
117
118
119 void
120 CallInputEvent(IWidget& wgt, const String& str, const KeyInput& k)
121 {
122 CallEvent<TextInput>(wgt, TextInputEventArgs(wgt, str, k));
123 }
124
125
126 bool
127 GUIState::CheckHeldState(const KeyInput& keys, InputTimer::HeldStateType& s)
128 {
129 if(checked_held.none())
130 checked_held = keys;
131 else if(checked_held != keys)
132 {
133 yunseq(checked_held |= keys, s = InputTimer::Free);
134 return true;
135 }
136 return {};
137 }
138
139 bool
140 GUIState::CheckDraggingOffset(observer_ptr<IWidget> p)
141 {
142 if(!p)
143 p = p_indp_focus;
144 if(p)
145 {
146 if(DraggingOffset == Vec::Invalid)
147 DraggingOffset = GetLocationOf(*p) - CursorLocation;
148 else
149 return true;
150 }
151 return {};
152 }
153
154 void
155 GUIState::CleanupReferences(IWidget& wgt)
156 {
157 // TODO: Simplify using %unseq_apply?
158 if(p_CursorOver.get() == &wgt)
159 p_CursorOver = {};
160 if(p_indp_focus.get() == &wgt)
161 p_indp_focus = {};
162 if(p_cascade_focus.get() == &wgt)
163 p_cascade_focus = {};
164 if(p_entered_toplevel.get() == &wgt)
165 p_entered_toplevel = {};
166 if(ExternalTextInputFocusPtr.get() == &wgt)
167 ExternalTextInputFocusPtr = {};
168 }
169
170 void
171 GUIState::HandleCascade(RoutedEventArgs& e, IWidget& wgt)
172 {
173 if(p_cascade_focus.get() != &wgt && IsFocused(e.GetSender()))
174 {
175 if(p_cascade_focus)
176 CallEvent<LostFocus>(*p_cascade_focus, e);
177 p_cascade_focus = make_observer(&wgt);
178 CallEvent<GotFocus>(*p_cascade_focus, e);
179 }
180 }
181
182 size_t
183 GUIState::RefreshTap(const Point& pt, size_t n, const Timers::Duration& delay)
184 {
185 const auto taps(TapTimer.RefreshTap(tap_count, delay));
186
187 tap_count = taps < n ? max<size_t>(taps, 1) : 0;
188 if(pt != Point::Invalid)
189 {
190 if(tap_count == 0 || taps == 0 || tap_location == Point::Invalid)
191 tap_location = pt;
192 else if(!Rect(-SPos(TapArea.Width) / 2, -SPos(TapArea.Height) / 2,
193 TapArea).Contains(pt - tap_location))
194 {
195 tap_location = Point::Invalid;
196 return 0;
197 }
198 }
199 return taps;
200 }
201
202 void
203 GUIState::Reset()
204 {
205 yunseq(KeyHeldState = InputTimer::Free, TouchHeldState = InputTimer::Free,
206 DraggingOffset = Vec::Invalid),
207 HeldTimer.ResetInput(), TapTimer.ResetInput();
208 yunseq(CursorLocation = Point::Invalid, CursorOverLocation = Point::Invalid,
209 p_CursorOver = {}, p_cascade_focus = {}, p_entered_toplevel = {},
210 p_indp_focus = {}, entered = {}, checked_held = {}, master_key = 0,
211 ExternalTextInputFocusPtr = {}, CaretLocation = Point::Invalid,
212 tap_location = Point::Invalid, tap_count = 0);
213 }
214
215 void
216 GUIState::ResetHeldState(InputTimer::HeldStateType& s, const KeyInput& k)
217 {
218 checked_held &= ~k;
219 if(checked_held.none())
220 {
221 s = InputTimer::Free,
222 HeldTimer.ResetInput();
223 }
224 }
225
226 void
227 GUIState::ResponseCursor(CursorEventArgs& e, UI::VisualEvent op)
228 {
229 if(op == CursorOver)
230 CursorOverLocation = CursorLocation;
231 CursorLocation = e;
232
233 auto wgt_ref(ystdex::ref(e.GetSender()));
234 IWidget* p_con;
235
236 e.Strategy = UI::RoutedEventArgs::Tunnel;
237 while(true)
238 {
239 if(!IsVisibleEnabled(wgt_ref) || e.Handled)
240 return;
241 if(wgt_ref.get().GetView().HitChildren(e))
242 {
243 // NOTE: Following code simulates a %Control at (0, 0) is
244 // responsible directly without user-defined event handlers.
245 e.Strategy = RoutedEventArgs::Direct;
246 switch(op)
247 {
248 case TouchUp:
249 // NOTE: See %Wrap.
250 if(p_indp_focus)
251 {
252 e.SetSender(*p_indp_focus);
253 TryLeaving(std::move(e));
254 }
255 ResetHeldState(TouchHeldState, e.Keys),
256 DraggingOffset = Vec::Invalid;
257 if(p_indp_focus)
258 CallEvent<ClickAcross>(*p_indp_focus, e);
259 p_indp_focus = {};
260 break;
261 case TouchDown:
262 // NOTE: See %Wrap.
263 p_indp_focus = {};
264 // NOTE: See %OnTouchDown_RequestToFrontFocused.
265 ClearFocusingOf(wgt_ref);
266 break;
267 // TODO: %CursorOver.
268 case TouchHeld:
269 // NOTE: See %ResponseCursorBase.
270 if(p_indp_focus)
271 CallEvent<TouchHeld>(*p_indp_focus, e);
272 // TODO: Send %Leave event.
273 default:
274 break;
275 }
276 if(e.Handled)
277 return;
278 e.Strategy = RoutedEventArgs::Bubble;
279 goto handle_bubbled;
280 }
281
282 auto& t(FetchTopEnabledAndVisibleWidget(wgt_ref, e, op));
283
284 if(&t == &wgt_ref.get())
285 break;
286 ResponseCursorBase(e, op);
287 e.SetSender(wgt_ref = t);
288 e.Position -= GetLocationOf(wgt_ref);
289 }
290 e.Strategy = RoutedEventArgs::Direct;
291 e.SetSender(wgt_ref);
292 ResponseCursorBase(e, op);
293 if(op == TouchDown)
294 HandleCascade(e, wgt_ref);
295 e.Strategy = RoutedEventArgs::Bubble;
296 while(!e.Handled && (p_con = FetchContainerPtr(wgt_ref).get()))
297 {
298 e.Position += GetLocationOf(wgt_ref);
299 e.SetSender(wgt_ref = *p_con);
300 handle_bubbled:
301 ResponseCursorBase(e, op);
302 }
303 }
304
305 void
306 GUIState::ResponseCursorBase(CursorEventArgs& e, UI::VisualEvent op)
307 {
308 auto& controller(e.GetSender().GetController());
309
310 switch(op)
311 {
312 case TouchHeld:
313 if(e.Strategy == RoutedEventArgs::Direct && p_indp_focus)
314 CallEvent<TouchHeld>(*p_indp_focus, e);
315 break;
316 case TouchUp:
317 case TouchDown:
318 case CursorOver:
319 DoEvent<HCursorEvent>(controller, op, std::move(e));
320 break;
321 case CursorWheel:
322 DoEvent<HCursorWheelEvent>(controller, CursorWheel,
323 std::move(ystdex::polymorphic_downcast<CursorWheelEventArgs&>(e)));
324 break;
325 default:
326 YAssert(false, "Invalid operation found.");
327 }
328 }
329
330 void
331 GUIState::ResponseKey(KeyEventArgs& e, UI::VisualEvent op)
332 {
333 auto wgt_ref(ystdex::ref(e.GetSender()));
334
335 e.Strategy = RoutedEventArgs::Tunnel;
336 while(true)
337 {
338 if(!IsVisibleEnabled(wgt_ref) || e.Handled)
339 return;
340 if(const auto p = FetchFocusingPtr(wgt_ref))
341 if(IsVisibleEnabled(*p))
342 {
343 ResponseKeyBase(e, op);
344 e.SetSender(wgt_ref = *p);
345 continue;
346 }
347 break;
348 }
349 e.Strategy = RoutedEventArgs::Direct;
350 e.SetSender(wgt_ref);
351 ResponseKeyBase(e, op);
352 if(op == KeyDown)
353 HandleCascade(e, wgt_ref);
354 e.Strategy = RoutedEventArgs::Bubble;
355 while(!e.Handled)
356 if(const auto p_con = FetchContainerPtr(wgt_ref))
357 {
358 e.SetSender(wgt_ref = *p_con);
359 ResponseKeyBase(e, op);
360 }
361 else
362 break;
363 }
364
365 void
366 GUIState::ResponseKeyBase(KeyEventArgs& e, UI::VisualEvent op)
367 {
368 YAssert(op == KeyUp || op == KeyDown || op == KeyHeld,
369 "Invalid operation found.");
370 DoEvent<HKeyEvent>(e.GetSender().GetController(), op, std::move(e));
371 }
372
373 bool
374 GUIState::SendInput(const KeyInput& k, const String& str)
375 {
376 return ExternalTextInputFocusPtr ? (UI::CallInputEvent(
377 *ExternalTextInputFocusPtr, str, k), true) : false;
378 }
379
380 void
381 GUIState::TryEntering(CursorEventArgs&& e)
382 {
383 if(!entered)
384 {
385 CallEvent<Enter>(e.GetSender(), e);
386 entered = true;
387 }
388 }
389
390 void
391 GUIState::TryLeaving(CursorEventArgs&& e)
392 {
393 if(entered)
394 {
395 CallEvent<Leave>(e.GetSender(), e);
396 entered = {};
397 }
398 }
399
400 char
401 GUIState::UpdateChar(const KeyInput& keys)
402 {
403 if(keys != checked_held)
404 {
405 master_key = FindFirstKeyInCategroy(keys, KeyCategory::Character);
406
407 yunseq(checked_held |= keys, KeyHeldState = InputTimer::Free);
408 }
409 return ExternalTextInputFocusPtr ? char()
410 : MapKeyChar(checked_held, master_key);
411 }
412
413 void
414 GUIState::Wrap(IWidget& wgt)
415 {
416 auto& controller(wgt.GetController());
417
418 yunseq(
419 FetchEvent<KeyUp>(controller).Add([this](KeyEventArgs&& e){
420 auto& sender(e.GetSender());
421
422 master_key
423 = FindFirstKeyInCategroy(checked_held, KeyCategory::Character);
424 ResetHeldState(KeyHeldState, e.Keys);
425 if(p_indp_focus.get() == &sender)
426 CallEvent<KeyPress>(sender, e);
427 p_indp_focus = {};
428 }, 0x00),
429 FetchEvent<KeyDown>(controller).Add([this](KeyEventArgs&& e){
430 p_indp_focus = make_observer(&e.GetSender());
431 }, 0xFF),
432 FetchEvent<CursorOver>(controller).Add([this](CursorEventArgs&& e){
433 if(e.Strategy == RoutedEventArgs::Direct)
434 {
435 e.Keys.reset();
436
437 auto& sender(e.GetSender());
438
439 if(p_CursorOver.get() != &sender || WidgetIdentity != shared_wgt_id)
440 {
441 if(p_CursorOver)
442 {
443 Point pt;
444
445 if(p_entered_toplevel.get() == &FetchTopLevel(sender, pt))
446 CallEvent<Leave>(*p_CursorOver, CursorEventArgs(sender,
447 e.Keys, e.Position - entered_top_location + pt));
448 }
449 CallEvent<Enter>(sender, CursorEventArgs(e));
450 entered_top_location = {};
451 p_entered_toplevel = make_observer(&FetchTopLevel(sender,
452 entered_top_location));
453 yunseq(p_CursorOver = make_observer(&sender),
454 shared_wgt_id = WidgetIdentity);
455 }
456 }
457 }, 0xFF),
458 FetchEvent<TouchUp>(controller).Add([this](CursorEventArgs&& e){
459 if(e.Strategy == RoutedEventArgs::Direct)
460 {
461 auto& sender(e.GetSender());
462
463 if(p_indp_focus)
464 {
465 e.SetSender(*p_indp_focus);
466 TryLeaving(std::move(e));
467 e.SetSender(sender);
468 }
469 ResetHeldState(TouchHeldState, e.Keys),
470 DraggingOffset = Vec::Invalid;
471 if(p_indp_focus.get() == &sender)
472 CallEvent<Click>(sender, e);
473 else if(p_indp_focus)
474 CallEvent<ClickAcross>(*p_indp_focus, e);
475 p_indp_focus = {};
476 }
477 }, 0x00),
478 FetchEvent<TouchDown>(controller).Add([this](CursorEventArgs&& e){
479 if(e.Strategy == RoutedEventArgs::Direct)
480 {
481 p_indp_focus = make_observer(&e.GetSender());
482 TryEntering(std::move(e));
483 }
484 }, 0xFF),
485 FetchEvent<TouchHeld>(controller).Add([this](CursorEventArgs&& e){
486 if(e.Strategy == RoutedEventArgs::Direct)
487 {
488 auto& sender(e.GetSender());
489
490 if(p_indp_focus.get() == &sender)
491 TryEntering(CursorEventArgs(e));
492 // else
493 // TryLeaving(CursorEventArgs(*p_indp_focus, e.Keys,
494 // e.Position - LocateForWidget(wgt, *p_indp_focus)));
495 else if(entered)
496 {
497 CallEvent<Leave>(*p_indp_focus,
498 CursorEventArgs(*p_indp_focus, e.Keys,
499 e.Position - LocateForWidget(sender, *p_indp_focus)));
500 entered = {};
501 }
502 }
503 }, 0xFF)
504 );
505 }
506
507 GUIState&
508 FetchGUIState() ynothrow
509 {
510 static_assert(std::is_nothrow_default_constructible<GUIState>(),
511 "State class is invalid");
512 static GUIState state;
513
514 return state;
515 }
516
517
518 GUIConfiguration&
519 FetchGUIConfiguration()
520 {
521 static GUIConfiguration conf;
522
523 return conf;
524 }
525
526 } // namespace UI;
527
528 } // namespace YSLib;
529
platform::p
while p p
Definition: YCLib/FileSystem.h:225
yconstexpr
#define yconstexpr
指定编译时常量表达式。
Definition: ydef.h:653
YSLib::UI::CursorWheel
指针设备滚轮输入。
Definition: YWidgetEvent.h:408
ynothrow
#define ynothrow
YSLib 无异常抛出保证：若支持 noexcept 关键字， 指定特定的 noexcept 异常规范。
Definition: ydef.h:740
YSLib::Activate
Activate
Definition: YApplication.h:175
YSLib::UI::CursorState::Pressed
YSLib::UI::FetchGUIState
GUIState & FetchGUIState() ynothrow
Definition: YGUI.cpp:508
platform::KeyCategory::Character
表示产生字符的键。
Definition: Keys.h:111
YSLib::UI::IsFocused
YF_API bool IsFocused(const IWidget &)
判断部件是否取得焦点。
Definition: YFocus.cpp:38
YSLib
YSLib 命名空间。
Definition: AndroidHost.h:45
CHRLib::e
const char const char * e
Definition: CharacterProcessing.h:109
YSLib::UI::InputTimer::Duration
Timers::Duration Duration
Definition: YGUI.h:51
YSLib::UI::InputTimer::DefaultDuration
static yconstexpr const Duration DefaultDuration
Definition: YGUI.h:64
YSLib::UI::FetchGUIConfiguration
GUIConfiguration & FetchGUIConfiguration()
Definition: YGUI.cpp:519
CHRLib::d
_tDst * d
Definition: CharacterProcessing.h:139
YSLib::UI::KeyHeld
键接触保持。
Definition: YWidgetEvent.h:388
YSLib::Drawing::PDefH
yconstfn PDefH(Size, Transpose, const Size &s) ynothrow ImplRet(
转置。
Definition: YGDIBase.h:516
YSLib::UI::TouchHeld
屏幕接触保持。
Definition: YWidgetEvent.h:392
YSLib::Timers::Duration
HighResolutionClock::duration Duration
高精度时间间隔。
Definition: YClock.h:68
YSLib::UI::RoutedEventArgs::Direct
直接事件：仅当遍历至目标控件时触发。
Definition: YWidgetEvent.h:109
YSLib::UI::CallInputEvent
YF_API void CallInputEvent(IWidget &, const String &, const KeyInput &)
调用指定部件的 TextInput 事件处理指定的输入。
Definition: YGUI.cpp:120
YSLib::Text::String
YSLib 标准字符串（使用 UCS-2 作为内部编码）。
Definition: YString.h:46
YSLib::UI::InputTimer
输入计时器。
Definition: YGUI.h:48
YSLib::UI::InputTimer::HeldStateType
HeldStateType
输入保持状态。
Definition: YGUI.h:56
YSLib::UI::IsVisible
IsVisible
Definition: YWidget.h:101
YSLib::UI::RoutedEventArgs::Bubble
气泡事件：向上遍历视图树时触发。
Definition: YWidgetEvent.h:107
YSLib::size_t
name const ValueNode::Container const string &name ynothrow observer_ptr< ValueNode::Container > const string &name ynothrow observer_ptr< const ValueNode::Container > const string &name ynothrow size_t
Definition: ValueNode.h:639
YSLib::ynothrow
YB_PURE const _type size_t n ynothrow
Definition: YCoreUtilities.h:168
platform_ex::Reset
unsigned long Reset(COMPtr< _iCOM > &ptr) ynothrow
Definition: COM.h:408
YB_UNLIKELY
#define YB_UNLIKELY(_expr)
分支预测提示。
Definition: ydef.h:438
cast.hpp
C++ 转换模板。
YSLib::UI::ClearFocusingOf
wgt wgt YF_API void ClearFocusingOf(IWidget &)
清除焦点指针并以此部件作为事件源调用被清除焦点部件的 LostFocus 事件。
Definition: YFocus.cpp:111
platform::SPos
std::int16_t SPos
屏幕坐标度量。
Definition: Video.h:42
YSLib::YAssert
YAssert(!(v< Deref(a)),"Value less than lower bound found.")
yunseq
#define yunseq
无序列依赖表达式组求值。
Definition: ydef.h:996
YSLib::UI::wgt
IWidget & wgt
Definition: YControl.h:112
YSLib::UI::InputTimer::InputTimer
InputTimer(Duration=DefaultDuration) ynothrow
Definition: YGUI.cpp:69
YSLib::UI::FetchFocusingPtr
pt pt Y FetchFocusingPtr
Definition: YWidget.h:145
YSLib::UI::RoutedEventArgs::Tunnel
隧道事件：向下遍历视图树时触发。
Definition: YWidgetEvent.h:108
YSLib::Text::str
const char const char * str
Definition: TextFile.h:77
Drawing
UI
platform::KeyInput
std::bitset< KeyBitsetWidth > KeyInput
按键并行位宽。
Definition: Keys.h:67
YSLib::UI::TextInputEventArgs
文本输入事件参数。
Definition: YWidgetEvent.h:274
YSLib::Drawing::GBinaryGroup< SPos >::Invalid
static const GBinaryGroup Invalid
无效（不在屏幕坐标系中）对象。
Definition: YGDIBase.h:58
YSLib::Drawing::ImplDeDtor
ImplDeDtor(FontException) namespace
Definition: Font.cpp:80
YSLib::UI::RepeatHeld
YF_API bool RepeatHeld(InputTimer &, InputTimer::HeldStateType &, const Timers::Duration &, const Timers::Duration &)
向指定计时器传递参数，根据状态重复按键。
Definition: YGUI.cpp:109
YSLib::p_con
name const ValueNode::Container * p_con
Definition: ValueNode.h:627
YModules.h
YSLib::Drawing::Point
GBinaryGroup< SPos > Point
屏幕二维点（直角坐标表示）。
Definition: YGDIBase.h:278
YSLib::UI::TouchDown
屏幕接触开始。
Definition: YWidgetEvent.h:391
ystdex::make_observer
observer_ptr< _type > make_observer(_type *p)
Definition: memory.hpp:1042
YSLib::UI::VisualEvent
VisualEvent
标准控件事件空间。
Definition: YWidgetEvent.h:370
YSLib::char
char
Definition: ValueNode.h:851
YSLib::UI::GetLocationOf
pt pt Y const IWidget &wgt GetLocationOf
Definition: YWidget.h:153
YSLib::UI::TouchUp
屏幕接触结束。
Definition: YWidgetEvent.h:390
YSLib::ImplRet
ImplRet(FetchGlobalInstance().GetGUIHostRef()) YF_API void Execute(GUIApplication &
执行程序主消息循环。
YSLib::UI::CursorOver
指针设备光标悬停。
Definition: YWidgetEvent.h:403
YSLib::UI::KeyDown
键接触开始。
Definition: YWidgetEvent.h:387
CHRLib::s
_tDst const _tSrc * s
Definition: CharacterProcessing.h:140
YSLib::UI::InputTimer::RefreshHeld
bool RefreshHeld(HeldStateType &, Duration=Timers::TimeSpan(240), Duration=Timers::TimeSpan(120))
重复检测输入接触保持状态。
Definition: YGUI.cpp:74
YSLib::UI::KeyUp
键接触结束。
Definition: YWidgetEvent.h:386
YSLib::UI::InputTimer::Free
Definition: YGUI.h:58
platform::KeyCategory::FindFirstKeyInCategroy
YF_API KeyIndex FindFirstKeyInCategroy(const KeyInput &, KeyIndex) ynothrow
找到第一个在指定类别的按键编码。
Definition: Keys.cpp:1115
platform::n
size_t n
Definition: YCLib/FileSystem.cpp:321
ystdex::ref
lref< _type > ref(_type &t)
构造引用包装。
Definition: ref.hpp:120