YSTest: scroll.cpp 源文件

YSTest PreAlpha_b400_20130424 The YSLib Test Project

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

- 文件列表
- 文件成员

全部 类 命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义 组 页
scroll.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/scroll.h"
29 #include "YSLib/UI/ygui.h"
30 #include "YSLib/Core/ystorage.hpp"
31 #include <ystdex/algorithm.hpp>
32
33 using namespace ystdex;
34
35 YSL_BEGIN
36
37 YSL_BEGIN_NAMESPACE(UI)
38
39 namespace
40 {
41
42 pair<bool, bool>
43 FixScrollBarLayout(Size& d, const Size& s, SDst min_width, SDst min_height)
44 {
45 bool need_h(d.Width < s.Width), need_v(d.Height < s.Height);
46
47 if(need_h)
48 {
49 if(d.Height < min_height)
50 throw GeneralEvent("Scroll bar need more height.");
51 d.Height -= min_height;
52 }
53 if(need_v)
54 {
55 if(d.Width < min_width)
56 throw GeneralEvent("Scroll bar need more width.");
57 d.Width -= min_width;
58 }
59 if(need_h != need_v)
60 {
61 if(!need_h && d.Width < s.Width)
62 {
63 need_h = true;
64 if(d.Height < min_height)
65 throw GeneralEvent("Scroll bar need more height.");
66 d.Height -= min_height;
67 }
68 if(!need_v && d.Height < s.Height)
69 {
70 need_v = true;
71 if(d.Width < min_width)
72 throw GeneralEvent("Scroll bar need more width.");
73 d.Width -= min_width;
74 }
75 }
76 return pair<bool, bool>(need_h, need_v);
77 }
78
79 const SDst defMinScrollBarWidth(16);
80 const SDst defMinScrollBarHeight(16);
81
82 } //unnamed namespace;
83
84
85 ATrack::ATrack(const Rect& r, SDst uMinThumbLength)
86 : Control({r.GetPoint(), max<SDst>(defMinScrollBarWidth, r.Width),
87 max<SDst>(defMinScrollBarHeight, r.Height)}, NoBackgroundTag()),
88 GMRange<ValueType>(0xFF, 0),
89 tmbScroll(Size(defMinScrollBarWidth, defMinScrollBarHeight)),
90 min_thumb_length(uMinThumbLength), large_delta(min_thumb_length)
91 {
92 SetContainerPtrOf(tmbScroll, this);
93 yunseq(
94 Background = std::bind(DrawTrackBackground, std::placeholders::_1,
95 std::ref(*this)),
96 GetThumbDrag() += [this](UIEventArgs&&){
97 LocateThumb(0, ScrollCategory::ThumbTrack);
98 },
99 FetchEvent<TouchMove>(*this) += OnTouchMove,
100 FetchEvent<TouchDown>(*this) += [this](TouchEventArgs&& e){
101 if(e.Strategy == RoutedEventArgs::Direct && &e.GetSender() == this
102 && Rect(GetSizeOf(*this)).Contains(e))
103 {
104 ScrollCategory t;
105
106 switch(CheckArea(e.GetRef(IsHorizontal())))
107 {
108 case OnPrev:
109 t = ScrollCategory::LargeDecrement;
110 break;
111 case OnNext:
112 t = ScrollCategory::LargeIncrement;
113 break;
114 case None:
115 return;
116 default:
117 t = ScrollCategory::EndScroll;
118 }
119 LocateThumb(0, t);
120 }
121 }
122 );
123 }
124
125 void
126 ATrack::SetThumbLength(SDst l)
127 {
128 RestrictInInterval(l, min_thumb_length, GetTrackLength());
129
130 Size s(GetSizeOf(tmbScroll));
131 const bool is_h(IsHorizontal());
132
133 if(l != s.GetRef(is_h))
134 {
135 Invalidate(tmbScroll);
136 s.GetRef(is_h) = l;
137 SetSizeOf(tmbScroll, s);
138 }
139 }
140 void
141 ATrack::SetThumbPosition(SPos pos)
142 {
143 RestrictInClosedInterval(pos, 0, GetScrollableLength());
144
145 Point pt(GetLocationOf(tmbScroll));
146 const bool is_h(IsHorizontal());
147
148 if(pos != pt.GetRef(is_h))
149 {
150 Invalidate(tmbScroll);
151 pt.GetRef(is_h) = pos;
152 SetLocationOf(tmbScroll, pt);
153 }
154 }
155 void
156 ATrack::SetMaxValue(ValueType m)
157 {
158 if(m > 0)
159 {
160 if(large_delta > m)
161 large_delta = m;
162 max_value = m;
163 }
164 }
165 void
166 ATrack::SetValue(ValueType val)
167 {
168 value = val;
169 SetThumbPosition(SPos(round(val * GetScrollableLength() / max_value)));
170 }
171 void
172 ATrack::SetLargeDelta(ValueType val)
173 {
174 large_delta = val;
175 SetThumbLength(SDst(round(val * GetTrackLength() / (val + max_value))));
176 }
177
178 ATrack::Area
179 ATrack::CheckArea(SPos q) const
180 {
181 if(q >= 0)
182 {
183 yconstexpr Area lst[] = {OnPrev, OnThumb, OnNext};
184 const SPos a[] = {SPos(), SPos(GetThumbPosition()),
185 SPos(GetThumbPosition() + GetThumbLength())};
186 const size_t n(SwitchInterval(q, a, 3));
187
188 if(n < 3)
189 return lst[n];
190 }
191 return None;
192 }
193
194 void
195 ATrack::LocateThumb(ValueType val, ScrollCategory t)
196 {
197 ValueType old_value(value);
198
199 if(t == ScrollCategory::ThumbTrack)
200 value = GetThumbPosition() == GetScrollableLength() ? max_value
201 : max_value * GetThumbPosition()
202 / (GetTrackLength() - GetThumbLength());
203 else
204 {
205 if(t == ScrollCategory::LargeDecrement
206 || t == ScrollCategory::LargeIncrement)
207 val = GetLargeDelta();
208 switch(t)
209 {
210 case ScrollCategory::SmallDecrement:
211 case ScrollCategory::LargeDecrement:
212 if(value > val)
213 {
214 SetValue(value - val);
215 break;
216 }
217 case ScrollCategory::First:
218 value = 0;
219 SetThumbPosition(0);
220 break;
221 case ScrollCategory::SmallIncrement:
222 case ScrollCategory::LargeIncrement:
223 if(value + val < max_value)
224 {
225 SetValue(value + val);
226 break;
227 }
228 case ScrollCategory::Last:
229 value = max_value;
230 SetThumbPosition(GetScrollableLength());
231 default:
232 ;
233 }
234 }
235 GetScroll()(ScrollEventArgs(*this, t, value, old_value));
236 }
237
238
239 void
240 DrawTrackBackground(PaintEventArgs&& e, ATrack& trk)
241 {
242 const auto& g(e.Target);
243 const auto& pt(e.Location);
244 const Rect r(pt, GetSizeOf(trk));
245 auto& pal(FetchGUIState().Colors);
246
247 FillRect(g, r, pal[Styles::Track]);
248
249 #define YSL_UI_ATRACK_PARTIAL_INVALIDATION
250 // NOTE: Partial invalidation made no efficiency improved here.
251 const auto c(pal[Styles::Light]);
252 #ifdef YSL_UI_ATRACK_PARTIAL_INVALIDATION
253 SPos x(pt.X);
254 SPos y(pt.Y);
255 SPos xr(x + trk.GetWidth());
256 SPos yr(y + trk.GetHeight());
257 #else
258 const SPos xr(pt.X + trk.GetWidth());
259 const SPos yr(pt.Y + trk.GetHeight());
260 #endif
261
262 if(trk.IsHorizontal())
263 {
264 #ifdef YSL_UI_ATRACK_PARTIAL_INVALIDATION
265 RestrictInInterval(y, r.Y, r.Y + r.Height),
266 RestrictInInterval(yr, r.Y, r.Y + r.Height);
267 #endif
268 DrawHLineSeg(g, pt.Y, pt.X, xr, c),
269 DrawHLineSeg(g, yr, pt.X, xr, c);
270 }
271 else
272 {
273 #ifdef YSL_UI_ATRACK_PARTIAL_INVALIDATION
274 RestrictInInterval(x, r.X, r.X + r.Width),
275 RestrictInInterval(xr, r.X, r.X + r.Width);
276 #endif
277 DrawVLineSeg(g, pt.X, pt.Y, yr, c),
278 DrawVLineSeg(g, xr, pt.Y, yr, c);
279 }
280 #ifndef YSL_UI_ATRACK_PARTIAL_INVALIDATION
281 e.ClipArea = r;
282 #endif
283 }
284
285
286 HorizontalTrack::HorizontalTrack(const Rect& r, SDst uMinThumbLength)
287 : ATrack(r, uMinThumbLength)
288 {
289 YAssert(GetWidth() > GetHeight(), "Width is not greater than height.");
290
291 FetchEvent<TouchMove>(tmbScroll) +=[this](TouchEventArgs&& e){
292 if(e.Strategy == RoutedEventArgs::Direct)
293 {
294 auto& st(FetchGUIState());
295 SPos x(st.LastControlLocation.X + st.DraggingOffset.X);
296
297 RestrictInClosedInterval(x, 0, GetWidth() - tmbScroll.GetWidth());
298 Invalidate(tmbScroll);
299 SetLocationOf(tmbScroll, Point(x, GetLocationOf(tmbScroll).Y));
300 GetThumbDrag()(UIEventArgs(*this));
301 }
302 };
303 }
304
305
306 VerticalTrack::VerticalTrack(const Rect& r, SDst uMinThumbLength)
307 : ATrack(r, uMinThumbLength)
308 {
309 YAssert(GetHeight() > GetWidth(), "height is not greater than width.");
310
311 FetchEvent<TouchMove>(tmbScroll) += [this](TouchEventArgs&& e){
312 if(e.Strategy == RoutedEventArgs::Direct)
313 {
314 auto& st(FetchGUIState());
315 SPos y(st.LastControlLocation.Y + st.DraggingOffset.Y);
316
317 RestrictInClosedInterval(y, 0, GetHeight() - tmbScroll.GetHeight());
318 Invalidate(tmbScroll);
319 SetLocationOf(tmbScroll, Point(GetLocationOf(tmbScroll).X, y));
320 GetThumbDrag()(UIEventArgs(*this));
321 }
322 };
323 }
324
325
326 AScrollBar::AScrollBar(const Rect& r, SDst uMinThumbSize, Orientation o)
327 : Control(r, NoBackgroundTag()),
328 pTrack(o == Horizontal
329 ? static_cast<ATrack*>(new HorizontalTrack(
330 Rect(r.Height, 0, r.Width - r.Height * 2, r.Height), uMinThumbSize))
331 : static_cast<ATrack*>(new VerticalTrack(
332 Rect(0, r.Width, r.Width, r.Height - r.Width * 2), uMinThumbSize))),
333 btnPrev(Rect()), btnNext(Rect()), small_delta(2)
334 {
335 SetContainerPtrOf(*pTrack, this),
336 SetContainerPtrOf(btnPrev, this);
337 SetContainerPtrOf(btnNext, this);
338 yunseq(
339 FetchEvent<Resize>(*this) += [this](UIEventArgs&&){
340 auto& track(GetTrack());
341 const bool is_h(track.IsHorizontal());
342 const SDst prev_metric(GetSizeOf(btnPrev).GetRef(is_h));
343 const SDst sum(prev_metric + GetSizeOf(btnNext).GetRef(is_h));
344
345 YAssert(GetSizeOf(*this).GetRef(is_h) - sum > 0,
346 "No enough space for track.");
347
348 const SDst tl(GetSizeOf(*this).GetRef(is_h) - sum);
349
350 yunseq(track.GetView().GetSizeRef().GetRef(is_h) = tl, btnNext
351 .GetView().GetLocationRef().GetRef(is_h) = tl + prev_metric);
352 // NOTE: No event %(Resize, Move) is raised.
353 },
354 FetchEvent<KeyHeld>(*this) += OnKeyHeld,
355 FetchEvent<TouchMove>(btnPrev) += OnTouchMove,
356 FetchEvent<TouchDown>(btnPrev) += [this](TouchEventArgs&&){
357 LocateThumb(small_delta, ScrollCategory::SmallDecrement);
358 },
359 FetchEvent<TouchMove>(btnNext) += OnTouchMove,
360 FetchEvent<TouchDown>(btnNext) += [this](TouchEventArgs&&){
361 LocateThumb(small_delta, ScrollCategory::SmallIncrement);
362 },
363 FetchEvent<KeyUp>(*this) += OnKey_Bound_TouchUpAndLeave,
364 FetchEvent<KeyDown>(*this) += OnKey_Bound_EnterAndTouchDown
365 );
366
367 Size s(GetSizeOf(*this));
368 const bool bHorizontal(o == Horizontal);
369 const SDst l(s.GetRef(!bHorizontal));
370
371 s.GetRef(bHorizontal) = l;
372 SetSizeOf(btnPrev, s);
373 SetSizeOf(btnNext, s);
374 // Button.SetLocationOf(btnPrev, Point());
375 MoveToBottom(btnNext);
376 MoveToRight(btnNext);
377 }
378
379 HorizontalScrollBar::HorizontalScrollBar(const Rect& r, SDst uMinThumbLength)
380 : AScrollBar(r, uMinThumbLength, Horizontal)
381 {
382 using namespace std;
383 using namespace placeholders;
384
385 yunseq(
386 FetchEvent<Paint>(btnPrev) += bind(DrawArrow, _1, ref(btnPrev), 4,
387 RDeg180, ref(ForeColor)),
388 FetchEvent<Paint>(btnNext) += bind(DrawArrow, _1, ref(btnNext), 4,
389 RDeg0, ref(ForeColor))
390 );
391 }
392
393 IWidget*
394 HorizontalScrollBar::GetBoundControlPtr(const KeyInput& k)
395 {
396 if(k.count() == 1)
397 {
398 if(k[KeyCodes::Left])
399 return &btnPrev;
400 if(k[KeyCodes::Right])
401 return &btnNext;
402 }
403 return nullptr;
404 }
405
406
407 VerticalScrollBar::VerticalScrollBar(const Rect& r, SDst uMinThumbLength)
408 : AScrollBar(r, uMinThumbLength, Vertical)
409 {
410 using namespace std;
411 using namespace placeholders;
412
413 yunseq(
414 FetchEvent<Paint>(btnPrev) += bind(DrawArrow, _1, ref(btnPrev), 4,
415 RDeg90, ref(ForeColor)),
416 FetchEvent<Paint>(btnNext) += bind(DrawArrow, _1, ref(btnNext), 4,
417 RDeg270, ref(ForeColor))
418 );
419 }
420
421 IWidget*
422 VerticalScrollBar::GetBoundControlPtr(const KeyInput& k)
423 {
424 if(k.count() == 1)
425 {
426 if(k[KeyCodes::Up])
427 return &btnPrev;
428 if(k[KeyCodes::Down])
429 return &btnNext;
430 }
431 return nullptr;
432 }
433
434
435 ScrollableContainer::ScrollableContainer(const Rect& r)
436 : Control(r),
437 hsbHorizontal(Size(r.Width, defMinScrollBarHeight)),
438 vsbVertical(Size(defMinScrollBarWidth, r.Height))
439 {
440 // TODO: Allow user to choose whether background is drawn.
441 SetContainerPtrOf(hsbHorizontal, this),
442 SetContainerPtrOf(vsbVertical, this);
443 MoveToBottom(hsbHorizontal);
444 MoveToRight(vsbVertical);
445 }
446
447 Size
448 ScrollableContainer::FixLayout(const Size& s)
449 {
450 Size arena(GetSizeOf(*this));
451
452 try
453 {
454 const pair<bool, bool> p(FixScrollBarLayout(arena, s,
455 defMinScrollBarWidth, defMinScrollBarHeight));
456
457 if(p.first && p.second && GetWidth() > defMinScrollBarWidth
458 && GetHeight() > defMinScrollBarHeight)
459 {
460 hsbHorizontal.SetWidth(GetWidth() - defMinScrollBarWidth);
461 vsbVertical.SetHeight(GetHeight() - defMinScrollBarHeight);
462 }
463 else if(p.first)
464 {
465 SetSizeOf(hsbHorizontal, Size(GetWidth(),
466 hsbHorizontal.GetHeight()));
467 MoveToBottom(hsbHorizontal);
468 }
469 else if(p.second)
470 {
471 SetSizeOf(vsbVertical, Size(vsbVertical.GetWidth(),
472 GetHeight()));
473 MoveToRight(vsbVertical);
474 }
475 SetVisibleOf(hsbHorizontal, p.first);
476 SetVisibleOf(vsbVertical, p.second);
477 }
478 catch(GeneralEvent&)
479 {}
480 return arena;
481 }
482
483 YSL_END_NAMESPACE(UI)
484
485 YSL_END
486