YSTest  PreAlpha_b500_20140530
The YSLib Test Project
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
textlist.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_TextList
30 #include YFM_YSLib_UI_YWindow
31 #include YFM_YSLib_UI_Border
32 #include YFM_YSLib_Service_YBlit
33 #include YFM_YSLib_Service_TextLayout
34 
35 namespace YSLib
36 {
37 
38 using namespace Drawing;
39 
40 namespace UI
41 {
42 
43 namespace
44 {
45  const SDst defMarginH(2);
46  const SDst defMarginV(1);
47  const SDst defMinScrollBarWidth(16);
48 // const SDst defMinScrollBarHeight(16); //!< 默认最小滚动条高。
49 }
50 
51 
52 TextList::TextList(const Rect& r, const shared_ptr<ListType>& h,
53  const pair<Color, Color>& hilight_pair)
54  : Control(r, MakeBlankBrush()), MTextList(h), MHilightText(hilight_pair),
55  CyclicTraverse(false), viewer(GetListRef()), top_offset(0)
56 {
57  const auto invalidator([this]{
58  Invalidate(*this);
59  });
60 
61  Margin = Padding(defMarginH, defMarginH, defMarginV, defMarginV);
62  yunseq(
63  FetchEvent<KeyDown>(*this) += [this](KeyEventArgs&& e){
64  if(viewer.GetTotal() != 0)
65  {
66  using namespace KeyCodes;
67  const auto& k(e.GetKeys());
68 
69  if(k.count() != 1)
70  return;
71  if(k[Up] || k[Down] || k[PgUp] || k[PgDn])
72  {
73  const auto old_sel(viewer.GetSelectedIndex());
74  const auto old_off(viewer.GetOffset());
75  const auto old_hid(viewer.GetHeadIndex());
76  const auto old_top(top_offset);
77 
78  {
79  const bool up(k[Up] || k[PgUp]);
80 
81  if(viewer.IsSelected())
82  {
83  viewer.IncreaseSelected((up ? -1 : 1) * (k[Up]
84  || k[Down] ? 1 : GetHeight() / GetItemHeight()));
85  if(old_sel == viewer.GetSelectedIndex()
86  && CyclicTraverse)
87  goto bound_select;
88  if(viewer.GetOffset() == (up ? 0 : ViewerType
89  ::DifferenceType(viewer.GetLength() - 1)))
90  AdjustOffset(up);
91  }
92  else
93 bound_select:
94  up ? SelectLast() : SelectFirst();
95  }
96 
97  const auto new_off(viewer.GetOffset());
98 
99  if(viewer.GetSelectedIndex() != old_sel)
100  CallSelected();
101  if(old_top != top_offset || viewer.GetHeadIndex() != old_hid)
102  UpdateView(*this);
103  else if(old_off != new_off)
104  InvalidateSelected2(old_off, new_off);
105  }
106  else if(viewer.IsSelected())
107  {
108  // NOTE: Do not confuse with %UI::Enter.
109  if(k[KeyCodes::Enter])
110  InvokeConfirmed(viewer.GetSelectedIndex());
111  else if(k[Esc])
112  {
113  InvalidateSelected(viewer.GetOffset());
114  ClearSelected();
115  // TODO: Create new event for canceling selection.
116  CallSelected();
117  }
118  }
119  }
120  },
121  FetchEvent<KeyHeld>(*this) += OnKeyHeld,
122  FetchEvent<TouchDown>(*this) += [this](CursorEventArgs&& e){
123  SetSelected(e);
124  UpdateView(*this);
125  },
126  FetchEvent<TouchHeld>(*this) += [this](CursorEventArgs&& e){
127  if(&e.GetSender() == this)
128  {
129  SetSelected(e);
130  UpdateView(*this);
131  }
132  },
133  FetchEvent<Click>(*this) += [this](CursorEventArgs&& e){
135  },
136  FetchEvent<Paint>(*this).Add(BorderBrush(), BoundaryPriority),
137  FetchEvent<GotFocus>(*this) += invalidator,
138  FetchEvent<LostFocus>(*this) += invalidator
139  );
141 }
142 
143 SDst
144 TextList::GetFullViewHeight() const
145 {
146  return GetItemHeight() * viewer.GetTotal();
147 }
148 SDst
150 {
151  return GetItemHeight() * viewer.GetHeadIndex() + top_offset;
152 }
153 
154 void
155 TextList::SetList(const shared_ptr<ListType>& h)
156 {
157  if(h)
158  {
160  viewer.SetContainer(*h);
162  }
163 }
164 
165 void
166 TextList::SetSelected(ListType::size_type i)
167 {
168  if(viewer.Contains(i))
169  {
170  const auto old_off(viewer.GetOffset());
171 
172  if(viewer.SetSelectedIndex(i))
173  {
174  CallSelected();
175  InvalidateSelected2(old_off, viewer.GetOffset());
176  }
177  }
178 }
179 void
181 {
182  SetSelected(CheckPoint(x, y));
183 }
184 
185 SDst
187 {
188  if(GetFullViewHeight() > GetHeight())
189  {
190  viewer.RestrictSelected();
191 
192  if(is_top)
193  {
194  const auto d(top_offset);
195 
196  top_offset = 0;
198  return d;
199  }
200  else
201  {
202  const SDst item_height(GetItemHeight());
203  const auto d((GetHeight() + top_offset) % item_height);
204 
205  if(d != 0)
206  {
207  const auto tmp(top_offset + item_height - d);
208 
209  top_offset = tmp % item_height;
211  viewer.IncreaseHead(tmp / item_height);
212  }
213  return d;
214  }
215  }
216  return 0;
217 }
218 
219 void
221 {
222  const bool b(viewer.AdjustForContent());
223 
224  if(viewer.IsSelected() && b)
225  {
226  AdjustOffset(viewer.GetSelectedIndex() == viewer.GetHeadIndex());
227  return;
228  }
229  if(GetFullViewHeight() < GetViewPosition() + GetHeight())
230  top_offset = 0;
232 }
233 
234 void
236 {
237  const auto h(GetHeight());
238 
239  if(h != 0)
240  {
241  const auto ln_h(GetItemHeight());
242 
243  viewer.SetLength(h / ln_h + (top_offset != 0 || h % ln_h != 0));
244  }
245 }
246 
247 bool
248 TextList::CheckConfirmed(ListType::size_type idx) const
249 {
250  return viewer.IsSelected() && viewer.GetSelectedIndex() == idx;
251 }
252 
253 TextList::ListType::size_type
255 {
256  return Rect(GetSizeOf(*this)).Contains(x, y) ? (y + top_offset)
257  / GetItemHeight() + viewer.GetHeadIndex() : ListType::size_type(-1);
258 }
259 
260 void
261 TextList::InvalidateSelected(ListType::difference_type offset,
262  ListType::size_type n)
263 {
264  if(offset >= 0 && n != 0)
265  {
266  const auto ln_h(GetItemHeight());
267  Rect r(0, ln_h * offset - top_offset, GetWidth(), ln_h * n);
268 
269  if(r.Y < GetHeight())
270  {
271  r.Y = max<int>(0, r.Y);
272  RestrictUnsignedStrict(r.Height, GetHeight() - r.Y);
273  Invalidate(*this, r);
274  }
275  }
276 }
277 
278 void
279 TextList::InvalidateSelected2(ListType::difference_type x,
280  ListType::difference_type y)
281 {
282  if(y < x)
283  std::swap(x, y);
284  InvalidateSelected(x < 0 ? 0 : x, y - x + 1);
285 }
286 
287 void
289 {
290  RestrictInInterval(h, 0, GetFullViewHeight() - GetHeight());
291 
292  if(GetViewPosition() != h)
293  {
294  const SDst item_height(GetItemHeight());
295 
296  //先保证避免部分显示的项目使视图超长,再设置视图位置。
298  viewer.SetHeadIndex(h / item_height);
299  top_offset = h % item_height;
300  //更新视图。
301  UpdateView(*this, true);
302  }
303 }
304 
305 void
306 TextList::DrawItem(const Graphics& g, const Rect& mask, const Rect& unit,
307  ListType::size_type i)
308 {
309  Drawing::DrawClippedText(g, mask & (unit + Margin), tsList, GetList()[i],
310  false);
311 }
312 
313 void
314 TextList::DrawItemBackground(const PaintContext& pc, const Rect& r)
315 {
316  FillRectRaw<PixelType>(pc.Target.GetBufferPtr(), pc.Target.GetSize(),
317  pc.ClipArea & Rect(r.X + 1, r.Y, r.Width - 2, r.Height),
318  HilightBackColor);
319 }
320 
321 void
322 TextList::DrawItems(const PaintContext& pc)
323 {
324  const auto h(GetHeight());
325 
326  if(h != 0)
327  {
328  RefreshTextState();
329 
330  const Rect& r(pc.ClipArea);
331 
332  if(viewer.GetTotal() != 0 && bool(r))
333  {
334  const auto& g(pc.Target);
335  const auto& pt(pc.Location);
336  const auto ln_w(GetWidth());
337  const auto ln_h(GetItemHeight());
338 
339  //视图长度可能因为内容变化等原因改变,必须重新计算。
341 
342  const SPos lbound(r.Y - pt.Y);
343  const auto last(viewer.GetHeadIndex()
344  + min<ViewerType::SizeType>((lbound + r.Height + top_offset
345  - 1) / ln_h + 1, viewer.GetValid()));
346  SPos y(ln_h * ((min<SPos>(0, lbound) + top_offset - 1) / ln_h)
347  - top_offset);
348 
349  for(auto i(viewer.GetHeadIndex()); i < last; yunseq(y += ln_h, ++i))
350  {
351  SPos top(y), tmp(y + ln_h);
352 
353  RestrictInInterval<SPos>(top, 0, h);
354  RestrictInInterval<SPos>(tmp, 1, h + 1);
355  tmp -= top;
356 
357  const Rect unit(pt.X, top + pt.Y, ln_w, tmp);
358 
359  if(viewer.IsSelected() && i == viewer.GetSelectedIndex())
360  {
361  tsList.Color = HilightTextColor;
362  DrawItemBackground(pc, unit);
363  }
364  else
365  tsList.Color = ForeColor;
366  AdjustEndOfLine(tsList, unit + Margin, g.GetWidth()),
367  tsList.ResetPen(unit.GetPoint(), Margin);
368  if(y < 0)
369  tsList.Pen.Y -= top_offset;
370  DrawItem(g, pc.ClipArea, unit, i);
371  }
372  }
373  }
374 }
375 
376 void
378 {
379  DrawItems(e);
380 }
381 
382 void
384 {
385  bool b(viewer.IsSelected());
386 
387  viewer.Reset();
388  if(b)
390  top_offset = 0;
391  UpdateView(*this);
392 }
393 
394 void
396 {
398  AdjustOffset(true);
399 }
400 
401 void
403 {
404  viewer.SetSelectedIndex(GetList().size() - 1);
405  AdjustOffset(false);
406 }
407 
408 void
410 {
411  Selected(IndexEventArgs(*this, viewer.GetSelectedIndex()));
412 }
413 
414 void
415 TextList::InvokeConfirmed(ListType::size_type idx)
416 {
417  if(CheckConfirmed(idx))
418  Confirmed(IndexEventArgs(*this, idx));
419 }
420 
421 
422 void
423 ResizeForContent(TextList& tl)
424 {
425  SetSizeOf(tl, Size(tl.GetMaxTextWidth() + GetHorizontalOf(tl.Margin),
426  tl.GetFullViewHeight()));
427  tl.AdjustViewLength();
428 }
429 
430 void
431 UpdateView(TextList& tl, bool is_active)
432 {
433  tl.ViewChanged(TextList::ViewArgs(tl, is_active));
434  tl.AdjustViewLength();
435  Invalidate(tl);
436 }
437 
438 } // namespace UI;
439 
440 } // namespace YSLib;
441 
void InvalidateSelected(ListType::difference_type offset, ListType::size_type diff=1)
无效化偏移量对应的列表项区域。
Definition: textlist.cpp:261
typename _tCon::difference_type DifferenceType
项目索引差值类型。
Definition: viewer.hpp:56
ViewerType viewer
列表视图。
Definition: textlist.h:86
SDst GetViewPosition() const
取视图顶端竖直位置。
Definition: textlist.cpp:149
pt pt Y const IWidget &wgt const IWidget &wgt GetSizeOf
无效化:使相对于部件的子部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: ywidget.h:156
void Refresh(PaintEventArgs &&) override
刷新:按指定参数绘制界面并更新状态。
YF_API void OnKeyHeld(KeyEventArgs &&)
处理键接触保持事件。
Definition: ycontrol.cpp:108
GValueEventArgs< MTextList::IndexType > IndexEventArgs
索引事件。
Definition: textlist.h:47
SDst AdjustOffset(bool)
调整列表视图底项目的竖直偏移量为零。
Definition: textlist.cpp:186
YF_API void Invalidate(IWidget &, const Rect &)
无效化:使相对于部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: ywidget.cpp:111
YF_API void DrawClippedText(const Graphics &g, const Rect &mask, TextState &ts, const String &str, bool line_wrap)
绘制剪切区域的文本。
部件绘制参数。
Definition: ywgtevt.h:276
void UpdateView(TextList &tl, bool is_active)
Definition: textlist.cpp:431
bool SetSelectedIndex(SizeType t)
设置选中项目的索引。
Definition: viewer.hpp:171
void InvalidateSelected2(ListType::difference_type, ListType::difference_type)
无效化偏移量对应的列表项区域。
Definition: textlist.cpp:279
SDst Height
宽和高。
Definition: ygdibase.h:258
void SelectLast()
选择最后一个项目。
Definition: textlist.cpp:402
YF_API void SetSizeOf(IWidget &, const Size &)
设置部件大小。
Definition: ywidget.cpp:83
bool SetHeadIndex(SizeType t)
设置视图中首个项目的索引。
Definition: viewer.hpp:140
yconstexpr EventPriority BoundaryPriority(0x60)
void SetList(const shared_ptr< ListType > &h)
设置文本列表。
Definition: label.h:215
void ResetView()
复位视图。
std::uint16_t SDst
屏幕坐标距离。
Definition: Video.h:39
std::int16_t SPos
屏幕坐标度量。
Definition: Video.h:38
virtual bool CheckConfirmed(ListType::size_type) const
检查列表中的指定项是否有效。
void swap(any &x, any &y)
交换对象。
Definition: any.h:729
virtual void DrawItemBackground(const PaintContext &, const Rect &r)
描画列表项背景。
Definition: textlist.cpp:314
bool CyclicTraverse
循环选择遍历。
Definition: textlist.h:83
void SetSelected(ListType::size_type)
按指定项目索引设置选中项目。
void SetList(const shared_ptr< ListType > &)
设置文本列表。
#define yunseq
无序列依赖表达式组求值。
Definition: ydef.h:748
bool Contains(int px, int py) const ynothrow
判断点 (px, py) 是否在矩形内或边上。
Definition: ygdibase.cpp:80
void SelectFirst()
选择第一个项目。
Definition: textlist.cpp:395
void RestrictUnsignedStrict(_type &u, unsigned b) ynothrow
约束无符号整数 u 在区间上界 b 内。
Definition: ycutil.h:299
屏幕标准矩形:表示屏幕矩形区域。
Definition: ygdibase.h:416
void LocateViewPosition(SDst)
定位视图顶端至指定竖直位置。
bool AdjustForContent()
按序列内容大小依次调整选中和首个项目的索引。
Definition: viewer.hpp:191
virtual void DrawItem(const Graphics &, const Rect &mask, const Rect &, ListType::size_type)
绘制列表项。
void AdjustViewForContent()
按内容大小依次调整视图中选中和首个项目的索引,然后按需调整竖直偏移量。
SDst top_offset
列表视图首项目超出上边界的竖直偏移量。
Definition: textlist.h:87
void RestrictInInterval(_type &i, int a, int b) ynothrow
约束整数 i 在左闭右开区间 [a, b) 中。
Definition: ycutil.h:283
bounds & r
Definition: ydraw.h:220
ListType::size_type CheckPoint(SPos, SPos)
检查点(相对于所在缓冲区的控件坐标)是否在选择范围内,
Definition: textlist.cpp:254
void InvokeConfirmed(ListType::size_type)
检查和调用确认事件处理器。
Definition: textlist.cpp:415
c yconstfn g
Definition: ystyle.h:104
void AdjustViewLength()
调整视图长度。
void CallSelected()
调用选中事件处理器。
Definition: textlist.cpp:409
void ResizeForContent(TextList &tl)
Definition: textlist.cpp:423
bool SetLength(SizeType l)
设置长度。
Definition: viewer.hpp:158
屏幕区域大小。
Definition: ygdibase.h:249
virtual void DrawItems(const PaintContext &)
绘制列表。
Definition: textlist.cpp:322
int int is_selected bool Contains(SizeType i) const
判断是否在有效范围内包含指定项目索引。
Definition: viewer.hpp:108
Selected const shared_ptr< ListType > const pair< Color, Color > Selected
Definition: textlist.h:122
GValueEventArgs< bool > ViewArgs
视图参数类型。
Definition: textlist.h:67