YSTest  PreAlpha_b500_20140530
The YSLib Test Project
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
scroll.cpp
浏览该文件的文档.
1 /*
2  © 2011-2014 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_Scroll
30 #include YFM_YSLib_UI_YGUI
31 #include YFM_YSLib_Core_YStorage
32 #include <ystdex/algorithm.hpp>
33 
34 using namespace ystdex;
35 
36 namespace YSLib
37 {
38 
39 namespace UI
40 {
41 
42 namespace
43 {
44 
45 pair<bool, bool>
46 FixScrollBarLayout(Size& d, const Size& s, SDst min_width, SDst min_height)
47 {
48  bool need_h(d.Width < s.Width), need_v(d.Height < s.Height);
49 
50  if(need_h)
51  {
52  if(d.Height < min_height)
53  throw GeneralEvent("Scroll bar need more height.");
54  d.Height -= min_height;
55  }
56  if(need_v)
57  {
58  if(d.Width < min_width)
59  throw GeneralEvent("Scroll bar need more width.");
60  d.Width -= min_width;
61  }
62  if(need_h != need_v)
63  {
64  if(!need_h && d.Width < s.Width)
65  {
66  need_h = true;
67  if(d.Height < min_height)
68  throw GeneralEvent("Scroll bar need more height.");
69  d.Height -= min_height;
70  }
71  if(!need_v && d.Height < s.Height)
72  {
73  need_v = true;
74  if(d.Width < min_width)
75  throw GeneralEvent("Scroll bar need more width.");
76  d.Width -= min_width;
77  }
78  }
79  return pair<bool, bool>(need_h, need_v);
80 }
81 
82 const SDst defMinScrollBarWidth(16);
83 const SDst defMinScrollBarHeight(16);
84 
85 } //unnamed namespace;
86 
87 
88 ATrack::ATrack(const Rect& r, SDst uMinThumbLength)
89  : Control({r.GetPoint(), max<SDst>(defMinScrollBarWidth, r.Width),
90  max<SDst>(defMinScrollBarHeight, r.Height)}),
91  GMRange<ValueType>(0xFF, 0),
92  tmbScroll(Size(defMinScrollBarWidth, defMinScrollBarHeight)),
93  min_thumb_length(uMinThumbLength), large_delta(min_thumb_length)
94 {
95  SetContainerPtrOf(tmbScroll, this);
96  yunseq(
97  Background = std::bind(DrawTrackBackground, std::placeholders::_1,
98  std::ref(*this)),
99  GetThumbDrag() += [this]{
100  LocateThumb(0, ScrollCategory::ThumbTrack);
101  },
102  FetchEvent<TouchHeld>(*this) += OnTouchHeld,
103  FetchEvent<TouchDown>(*this) += [this](CursorEventArgs&& e){
104  if(e.Strategy == RoutedEventArgs::Direct && &e.GetSender() == this
105  && Rect(GetSizeOf(*this)).Contains(e))
106  {
107  ScrollCategory t;
108 
109  switch(CheckArea(e.Position.GetRef(IsHorizontal())))
110  {
111  case OnPrev:
112  t = ScrollCategory::LargeDecrement;
113  break;
114  case OnNext:
115  t = ScrollCategory::LargeIncrement;
116  break;
117  case None:
118  return;
119  default:
120  t = ScrollCategory::EndScroll;
121  }
122  LocateThumb(0, t);
123  }
124  }
125  );
126 }
127 
128 void
129 ATrack::SetThumbLength(SDst l)
130 {
131  RestrictInInterval(l, min_thumb_length, GetTrackLength());
132 
133  Size s(GetSizeOf(tmbScroll));
134  const bool is_h(IsHorizontal());
135 
136  if(l != s.GetRef(is_h))
137  {
138  Invalidate(tmbScroll);
139  s.GetRef(is_h) = l;
140  SetSizeOf(tmbScroll, s);
141  }
142 }
143 void
144 ATrack::SetThumbPosition(SPos pos)
145 {
146  RestrictInClosedInterval<SPos>(pos, 0, GetScrollableLength());
147 
148  Point pt(GetLocationOf(tmbScroll));
149  const bool is_h(IsHorizontal());
150 
151  if(pos != pt.GetRef(is_h))
152  {
153  Invalidate(tmbScroll);
154  pt.GetRef(is_h) = pos;
155  SetLocationOf(tmbScroll, pt);
156  }
157 }
158 void
159 ATrack::SetMaxValue(ValueType m)
160 {
161  if(m > 0)
162  {
163  if(large_delta > m)
164  large_delta = m;
165  max_value = m;
166  }
167 }
168 void
169 ATrack::SetValue(ValueType val)
170 {
171  value = val;
172  SetThumbPosition(SPos(round(val * GetScrollableLength() / max_value)));
173 }
174 void
175 ATrack::SetLargeDelta(ValueType val)
176 {
177  large_delta = val;
178  SetThumbLength(SDst(round(val * GetTrackLength() / (val + max_value))));
179 }
180 
182 ATrack::CheckArea(SPos q) const
183 {
184  if(q >= 0)
185  {
186  yconstexpr Area lst[]{OnPrev, OnThumb, OnNext};
187  const SPos a[]{SPos(), SPos(GetThumbPosition()),
188  SPos(GetThumbPosition() + GetThumbLength())};
189  const auto n(SwitchInterval(q, a, 3));
190 
191  if(n < 3)
192  return lst[n];
193  }
194  return None;
195 }
196 
197 void
198 ATrack::LocateThumb(ValueType val, ScrollCategory t)
199 {
200  ValueType old_value(value);
201 
202  if(t == ScrollCategory::ThumbTrack)
203  value = GetThumbPosition() == GetScrollableLength() ? max_value
204  : max_value * GetThumbPosition()
205  / (GetTrackLength() - GetThumbLength());
206  else
207  {
208  if(t == ScrollCategory::LargeDecrement
209  || t == ScrollCategory::LargeIncrement)
210  val = GetLargeDelta();
211  switch(t)
212  {
213  case ScrollCategory::SmallDecrement:
214  case ScrollCategory::LargeDecrement:
215  if(value > val)
216  {
217  SetValue(value - val);
218  break;
219  }
220  case ScrollCategory::First:
221  value = 0;
222  SetThumbPosition(0);
223  break;
224  case ScrollCategory::SmallIncrement:
225  case ScrollCategory::LargeIncrement:
226  if(value + val < max_value)
227  {
228  SetValue(value + val);
229  break;
230  }
231  case ScrollCategory::Last:
232  value = max_value;
233  SetThumbPosition(GetScrollableLength());
234  default:
235  ;
236  }
237  }
238  GetScroll()(ScrollEventArgs(*this, t, value, old_value));
239 }
240 
241 
242 void
244 {
245  const auto& g(e.Target);
246  const auto& pt(e.Location);
247  const Rect r(pt, GetSizeOf(trk));
248  auto& pal(FetchGUIState().Colors);
249 
250  FillRect(g, r, pal[Styles::Track]);
251 
252 #define YSL_UI_ATRACK_PARTIAL_INVALIDATION
253  // NOTE: Partial invalidation made no efficiency improved here.
254  const auto c(pal[Styles::Light]);
255 #ifdef YSL_UI_ATRACK_PARTIAL_INVALIDATION
256  SPos x(pt.X);
257  SPos y(pt.Y);
258  SPos xr(x + trk.GetWidth());
259  SPos yr(y + trk.GetHeight());
260 #else
261  const SPos xr(pt.X + trk.GetWidth());
262  const SPos yr(pt.Y + trk.GetHeight());
263 #endif
264 
265  if(trk.IsHorizontal())
266  {
267 #ifdef YSL_UI_ATRACK_PARTIAL_INVALIDATION
268  RestrictInInterval(y, r.Y, r.Y + r.Height),
269  RestrictInInterval(yr, r.Y, r.Y + r.Height);
270 #endif
271  DrawHLineSeg(g, r, pt.Y, pt.X, xr, c),
272  DrawHLineSeg(g, r, yr, pt.X, xr, c);
273  }
274  else
275  {
276 #ifdef YSL_UI_ATRACK_PARTIAL_INVALIDATION
277  RestrictInInterval(x, r.X, r.X + r.Width),
278  RestrictInInterval(xr, r.X, r.X + r.Width);
279 #endif
280  DrawVLineSeg(g, r, pt.X, pt.Y, yr, c),
281  DrawVLineSeg(g, r, xr, pt.Y, yr, c);
282  }
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  FetchEvent<TouchHeld>(tmbScroll) += [this](CursorEventArgs&& e){
291  if(e.Strategy == RoutedEventArgs::Direct)
292  {
293  auto& st(FetchGUIState());
294 
295  if(st.CheckDraggingOffset())
296  {
297  SPos x(st.CursorLocation.X + st.DraggingOffset.X);
298 
299  RestrictInClosedInterval<SPos>(x, 0,
300  GetWidth() - tmbScroll.GetWidth());
301  Invalidate(tmbScroll);
302  SetLocationOf(tmbScroll, Point(x, GetLocationOf(tmbScroll).Y));
303  GetThumbDrag()(UIEventArgs(*this));
304  }
305  }
306  };
307 }
308 
309 
310 VerticalTrack::VerticalTrack(const Rect& r, SDst uMinThumbLength)
311  : ATrack(r, uMinThumbLength)
312 {
313  YAssert(GetHeight() > GetWidth(), "height is not greater than width.");
314  FetchEvent<TouchHeld>(tmbScroll) += [this](CursorEventArgs&& e){
315  if(e.Strategy == RoutedEventArgs::Direct)
316  {
317  auto& st(FetchGUIState());
318 
319  if(st.CheckDraggingOffset())
320  {
321  SPos y(st.CursorLocation.Y + st.DraggingOffset.Y);
322 
323  RestrictInClosedInterval<SPos>(y, 0,
324  GetHeight() - tmbScroll.GetHeight());
325  Invalidate(tmbScroll);
326  SetLocationOf(tmbScroll, Point(GetLocationOf(tmbScroll).X, y));
327  GetThumbDrag()(UIEventArgs(*this));
328  }
329  }
330  };
331 }
332 
333 
334 AScrollBar::AScrollBar(const Rect& r, SDst uMinThumbSize, Orientation o)
335  : Control(r),
336  pTrack(o == Horizontal
337  ? static_cast<ATrack*>(new HorizontalTrack(
338  Rect(r.Height, 0, r.Width - r.Height * 2, r.Height), uMinThumbSize))
339  : static_cast<ATrack*>(new VerticalTrack(
340  Rect(0, r.Width, r.Width, r.Height - r.Width * 2), uMinThumbSize))),
341  btnPrev(Rect()), btnNext(Rect()), small_delta(2)
342 {
343  SetContainerPtrOf(*pTrack, this),
344  SetContainerPtrOf(btnPrev, this);
345  SetContainerPtrOf(btnNext, this);
346  yunseq(
347  FetchEvent<Resize>(*this) += [this](UIEventArgs&& e){
348  auto& track(GetTrackRef());
349  const bool is_h(track.IsHorizontal());
350  const SDst prev_metric(GetSizeOf(btnPrev).GetRef(is_h));
351  const SDst sum(prev_metric + GetSizeOf(btnNext).GetRef(is_h));
352 
353  YAssert(GetSizeOf(e.GetSender()).GetRef(is_h) - sum > 0,
354  "No enough space for track.");
355 
356  const SDst tl(GetSizeOf(e.GetSender()).GetRef(is_h) - sum);
357 
358  yunseq(track.GetView().GetSizeRef().GetRef(is_h) = tl,
359  btnNext.GetView().GetLocationRef().GetRef(is_h) = tl + prev_metric);
360  // NOTE: No event %(Resize, Move) is raised.
361  },
362  FetchEvent<KeyUp>(*this) += OnKey_Bound_TouchUp,
363  FetchEvent<KeyDown>(*this) += OnKey_Bound_TouchDown,
364  FetchEvent<KeyHeld>(*this) += OnKeyHeld,
365  FetchEvent<CursorWheel>(*this) += [this](CursorWheelEventArgs&& e){
366  if(e.GetDelta() > 0)
368  else if(e.GetDelta() < 0)
370  },
371  FetchEvent<TouchHeld>(btnPrev) += OnTouchHeld,
372  FetchEvent<TouchDown>(btnPrev) += [this]{
374  },
375  FetchEvent<TouchHeld>(btnNext) += OnTouchHeld,
376  FetchEvent<TouchDown>(btnNext) += [this]{
378  }
379  );
380 
381  Size s(GetSizeOf(*this));
382  const bool bHorizontal(o == Horizontal);
383  const SDst l(s.GetRef(!bHorizontal));
384 
385  s.GetRef(bHorizontal) = l;
386  SetSizeOf(btnPrev, s);
387  SetSizeOf(btnNext, s);
388 // Button.SetLocationOf(btnPrev, Point());
391 }
392 
393 void
394 AScrollBar::InitializeArrowPainters(Rotation prev, Rotation next)
395 {
396  using namespace std;
397  using namespace placeholders;
398 
399  yunseq(
400  FetchEvent<Paint>(btnPrev) += [this, prev](PaintEventArgs&& e){
401  DrawArrow(e.Target, e.ClipArea, 4, prev, ForeColor);
402  },
403  FetchEvent<Paint>(btnNext) += [this, next](PaintEventArgs&& e){
404  DrawArrow(e.Target, e.ClipArea, 4, next, ForeColor);
405  }
406  );
407 }
408 
409 
411  : AScrollBar(r, uMinThumbLength, Horizontal)
412 {
413  InitializeArrowPainters(RDeg180, RDeg0);
414 }
415 
416 IWidget*
418 {
419  if(k.count() == 1)
420  {
421  if(k[KeyCodes::Left])
422  return &btnPrev;
423  if(k[KeyCodes::Right])
424  return &btnNext;
425  }
426  return nullptr;
427 }
428 
429 
431  : AScrollBar(r, uMinThumbLength, Vertical)
432 {
433  InitializeArrowPainters(RDeg90, RDeg270);
434 }
435 
436 IWidget*
438 {
439  if(k.count() == 1)
440  {
441  if(k[KeyCodes::Up])
442  return &btnPrev;
443  if(k[KeyCodes::Down])
444  return &btnNext;
445  }
446  return nullptr;
447 }
448 
449 
451  : Control(r, MakeBlankBrush()),
452  hsbHorizontal(Size(r.Width, defMinScrollBarHeight)),
453  vsbVertical(Size(defMinScrollBarWidth, r.Height))
454 {
455  // TODO: Allow user to choose whether background is drawn.
456  SetContainerPtrOf(hsbHorizontal, this),
457  SetContainerPtrOf(vsbVertical, this),
460  FetchEvent<CursorWheel>(*this) += [this](CursorWheelEventArgs&& e){
462  CallEvent<CursorWheel>(vsbVertical, std::move(e));
463  };
464 }
465 
466 Size
468 {
469  Size arena(GetSizeOf(*this));
470 
471  try
472  {
473  const pair<bool, bool> p(FixScrollBarLayout(arena, s,
474  defMinScrollBarWidth, defMinScrollBarHeight));
475 
476  if(p.first && p.second && GetWidth() > defMinScrollBarWidth
477  && GetHeight() > defMinScrollBarHeight)
478  {
479  hsbHorizontal.SetWidth(GetWidth() - defMinScrollBarWidth);
480  vsbVertical.SetHeight(GetHeight() - defMinScrollBarHeight);
481  }
482  else if(p.first)
483  {
484  SetSizeOf(hsbHorizontal, Size(GetWidth(),
485  hsbHorizontal.GetHeight()));
487  }
488  else if(p.second)
489  {
491  GetHeight()));
493  }
494  SetVisibleOf(hsbHorizontal, p.first);
495  SetVisibleOf(vsbVertical, p.second);
496  }
497  catch(GeneralEvent&)
498  {}
499  return arena;
500 }
501 
502 } // namespace UI;
503 
504 } // namespace YSLib;
505 
VerticalTrack(const Rect &={}, SDst=8)
构造:使用指定边界和最小滑块长。
Definition: scroll.cpp:310
void DrawVLineSeg(const Graphics &g, const Rect &bounds, SPos x, SPos y1, SPos y2, Color c)
描画竖直线段。
Definition: ydraw.h:153
pt pt Y const IWidget &wgt const IWidget &wgt GetSizeOf
无效化:使相对于部件的子部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: ywidget.h:156
pt pt Y const IWidget &wgt GetLocationOf
Definition: ywidget.h:148
YF_API void OnKeyHeld(KeyEventArgs &&)
处理键接触保持事件。
Definition: ycontrol.cpp:108
YF_API void OnKey_Bound_TouchDown(KeyEventArgs &&)
处理按键事件:按键-指针设备接触开始。
Definition: ycontrol.cpp:171
YF_API void SetLocationOf(IWidget &, const Point &)
设置部件左上角所在位置(相对于容器的偏移坐标)。
Definition: ywidget.cpp:73
YF_API GUIState & FetchGUIState()
取默认图形用户界面公共状态。
Definition: ygui.cpp:442
YF_API void FillRect(const Graphics &g, const Rect &, Color c)
填充标准矩形。
Definition: ydraw.cpp:146
YF_API void Invalidate(IWidget &, const Rect &)
无效化:使相对于部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: ywidget.cpp:111
部件绘制参数。
Definition: ywgtevt.h:276
VerticalScrollBar(const Rect &={}, SDst=8)
Definition: scroll.cpp:430
ScrollableContainer(const Rect &={})
Definition: scroll.cpp:450
SDst Height
宽和高。
Definition: ygdibase.h:258
YF_API void SetSizeOf(IWidget &, const Size &)
设置部件大小。
Definition: ywidget.cpp:83
Size FixLayout(const Size &)
固定布局。
Definition: scroll.cpp:467
用户界面事件参数基类。
Definition: ywgtevt.h:59
ScrollCategory
滚动类别。
Definition: scroll.h:49
std::uint16_t SDst
屏幕坐标距离。
Definition: Video.h:39
std::int16_t SPos
屏幕坐标度量。
Definition: Video.h:38
水平轨道。
Definition: scroll.h:252
控件。
Definition: ycontrol.h:275
YF_API void MoveToBottom(IWidget &wgt)
移动部件 wgt 至容器下端。
Definition: yuicont.cpp:135
YF_API void MoveToRight(IWidget &wgt)
移动部件 wgt 至容器右端。
Definition: yuicont.cpp:120
YF_API void DrawTrackBackground(PaintEventArgs &&e, ATrack &)
绘制指定色调的基本按钮背景。
Definition: scroll.cpp:243
YF_API void OnTouchHeld(CursorEventArgs &&)
处理屏幕接触保持事件。
Definition: ycontrol.cpp:131
std::runtime_error GeneralEvent
一般运行时异常事件类。
Definition: yexcept.h:51
ValueType small_delta
小距离滚动偏移量:滚动事件关联的滑块位置变化绝对值。
Definition: scroll.h:319
VerticalScrollBar vsbVertical
控制竖直可视区域的竖直滚动条。
Definition: scroll.h:431
unique_ptr< ATrack > pTrack
Definition: scroll.h:302
Thumb btnNext
后滚动条按钮。
Definition: scroll.h:316
GBinaryGroup< SPos > Point
屏幕二维点(直角坐标表示)。
Definition: ygdibase.h:235
#define yunseq
无序列依赖表达式组求值。
Definition: ydef.h:748
滚动框小距离减量移动。
竖直轨道。
Definition: scroll.h:273
void DrawHLineSeg(const Graphics &g, const Rect &bounds, SPos y, SPos x1, SPos x2, Color c)
描画水平线段。
Definition: ydraw.h:128
ATrack *pTrack GetTrackRef()) DefGetterMem(const ynothrow
明亮背景。
Definition: ystyle.h:188
屏幕标准矩形:表示屏幕矩形区域。
Definition: ygdibase.h:416
Orientation
二元方向。
Definition: ygdibase.h:880
直接事件:仅当遍历至目标控件时触发。
Definition: ywgtevt.h:104
const IWidget &wgt SPos
Definition: ywidget.h:104
滚轮事件参数。
Definition: ywgtevt.h:217
Area
样式区域类型。
Definition: ystyle.h:177
Thumb btnPrev
前滚动条按钮。
Definition: scroll.h:310
表示未分配或保留的键。
Definition: Keys.h:99
p1 p1 Y
Definition: ydraw.h:188
HorizontalScrollBar hsbHorizontal
控制水平可视区域的水平滚动条。
Definition: scroll.h:426
HorizontalScrollBar(const Rect &={}, SDst=8)
Definition: scroll.cpp:410
Rotation
逆时针旋转角度指示输出指向。
Definition: ygdibase.h:868
泛型算法。
#define yconstexpr
指定编译时常量表达式。
Definition: ydef.h:462
YF_API void DrawArrow(const Graphics &, const Rect &, SDst=4, Rotation=RDeg0, Color=ColorSpace::Black)
在指定图形接口上下文上描画箭头。
Definition: ystyle.cpp:100
YF_API void OnKey_Bound_TouchUp(KeyEventArgs &&)
处理按键事件:按键-指针设备接触结束。
Definition: ycontrol.cpp:159
void RestrictInInterval(_type &i, int a, int b) ynothrow
约束整数 i 在左闭右开区间 [a, b) 中。
Definition: ycutil.h:283
bounds & r
Definition: ydraw.h:220
if(YB_UNLIKELY(r >=sGraphics.Height)) throw std return pBuffer r *sGraphics Width
Definition: ygdibase.cpp:155
c yconstfn g
Definition: ystyle.h:104
Color ForeColor
默认前景色。
Definition: ywidget.h:375
AScrollBar(const Rect &={}, SDst=8, Orientation=Horizontal)
构造:使用指定边界、大小和方向。
Definition: scroll.cpp:334
AController *controller_ptr Renderer *renderer_ptr View *view_ptr GetView()) DefGetterMem(const ynothrow
滚动条背景。
Definition: ystyle.h:184
屏幕区域大小。
Definition: ygdibase.h:249
size_t SwitchInterval(_type v, const _type *a, size_t n) ynothrow
计算满足指定的值 v 在区间 [a[i], a[i + 1]) 内最小的 i 。
Definition: ycutil.h:221
指针设备输入事件参数类。
Definition: ywgtevt.h:183
std::bitset< KeyBitsetWidth > KeyInput
按键并行位宽。
Definition: Keys.h:68
#define YAssert(_expr, _msg)
Definition: cassert.h:73
滚动框小距离增量移动。
滚动条。
Definition: scroll.h:294