YSTest  PreAlpha_b400_20130424
The YSLib Test Project
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
textlist.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/textlist.h"
29 #include "YSLib/UI/ywindow.h"
30 #include "YSLib/UI/YBrush.h"
31 #include "YSLib/Service/YBlit.h"
33 
35 
36 using namespace Drawing;
37 
39 
40 namespace
41 {
42  const SDst defMarginH(2);
43  const SDst defMarginV(1);
44  const SDst defMinScrollBarWidth(16);
45 // const SDst defMinScrollBarHeight(16); //!< 默认最小滚动条高。
46 }
47 
48 
49 TextList::TextList(const Rect& r, const shared_ptr<ListType>& h,
50  pair<Color, Color> hilight_pair)
51  : Control(r), MTextList(h),
52  HilightBackColor(hilight_pair.first),
53  HilightTextColor(hilight_pair.second), CyclicTraverse(false),
54  viewer(GetListRef()), top_offset(0)
55 {
56  Margin = Padding(defMarginH, defMarginH, defMarginV, defMarginV);
57  yunseq(
58  FetchEvent<KeyDown>(*this) += [this](KeyEventArgs&& e){
59  if(viewer.GetTotal() != 0)
60  {
61  using namespace KeyCodes;
62  const auto& k(e.GetKeys());
63 
64  if(k.count() != 1)
65  return;
66  if(k[Up] || k[Down] || k[PgUp] || k[PgDn])
67  {
68  const auto old_sel(viewer.GetSelectedIndex());
69  const auto old_off(viewer.GetOffset());
70  const auto old_hid(viewer.GetHeadIndex());
71  const auto old_top(top_offset);
72 
73  {
74  const bool up(k[Up] || k[PgUp]);
75 
76  if(viewer.IsSelected())
77  {
78  viewer.IncreaseSelected((up ? -1 : 1) * (k[Up]
79  || k[Down] ? 1 : GetHeight()
80  / GetItemHeight()));
81  if(old_sel == viewer.GetSelectedIndex()
82  && CyclicTraverse)
83  goto bound_select;
84  if(viewer.GetOffset() == (up ? 0 : ViewerType
85  ::DifferenceType(viewer.GetLength() - 1)))
86  AdjustOffset(up);
87  }
88  else
89 bound_select:
90  up ? SelectLast() : SelectFirst();
91  }
92 
93  const auto new_off(viewer.GetOffset());
94 
95  if(viewer.GetSelectedIndex() != old_sel)
96  CallSelected();
97  if(old_top != top_offset || viewer.GetHeadIndex()
98  != old_hid)
99  UpdateView(*this);
100  else if(old_off != new_off)
101  InvalidateSelected2(old_off, new_off);
102  }
103  else if(viewer.IsSelected())
104  {
105  // NOTE: Do not confuse with %UI::Enter.
106  if(k[KeyCodes::Enter])
107  InvokeConfirmed(viewer.GetSelectedIndex());
108  else if(k[Esc])
109  {
110  InvalidateSelected(viewer.GetOffset());
111  ClearSelected();
112  // TODO: Create new event for canceling selection.
113  CallSelected();
114  }
115  }
116  }
117  },
118  FetchEvent<KeyHeld>(*this) += OnKeyHeld,
119  FetchEvent<TouchDown>(*this) += [this](TouchEventArgs&& e){
120  SetSelected(e);
121  UpdateView(*this);
122  },
123  FetchEvent<TouchMove>(*this) += [this](TouchEventArgs&& e){
124  if(&e.GetSender() == this)
125  {
126  SetSelected(e);
127  UpdateView(*this);
128  }
129  },
130  FetchEvent<Click>(*this) += [this](TouchEventArgs&& e){
131  InvokeConfirmed(CheckPoint(e));
132  },
133  FetchEvent<Paint>(*this).Add(BorderBrush(), BoundaryPriority)
134  );
135  AdjustViewLength();
136 }
137 
138 SDst
139 TextList::GetFullViewHeight() const
140 {
141  return GetItemHeight() * viewer.GetTotal();
142 }
143 SDst
144 TextList::GetViewPosition() const
145 {
146  return GetItemHeight() * viewer.GetHeadIndex() + top_offset;
147 }
148 
149 void
150 TextList::SetList(const shared_ptr<ListType>& h)
151 {
152  if(h)
153  {
154  MTextList::SetList(h);
155  viewer.SetContainer(*h);
156  AdjustViewLength();
157  }
158 }
159 
160 void
161 TextList::SetSelected(ListType::size_type i)
162 {
163  if(viewer.Contains(i))
164  {
165  const auto old_off(viewer.GetOffset());
166 
167  if(viewer.SetSelectedIndex(i))
168  {
169  CallSelected();
170  InvalidateSelected2(old_off, viewer.GetOffset());
171  }
172  }
173 }
174 void
175 TextList::SetSelected(SPos x, SPos y)
176 {
177  SetSelected(CheckPoint(x, y));
178 }
179 
180 SDst
181 TextList::AdjustOffset(bool is_top)
182 {
183  if(GetFullViewHeight() > GetHeight())
184  {
185  viewer.RestrictSelected();
186 
187  if(is_top)
188  {
189  const auto d(top_offset);
190 
191  top_offset = 0;
192  AdjustViewLength();
193  return d;
194  }
195  else
196  {
197  const SDst item_height(GetItemHeight());
198  const auto d((GetHeight() + top_offset) % item_height);
199 
200  if(d != 0)
201  {
202  const auto tmp(top_offset + item_height - d);
203 
204  top_offset = tmp % item_height;
205  AdjustViewLength();
206  viewer.IncreaseHead(tmp / item_height);
207  }
208  return d;
209  }
210  }
211  return 0;
212 }
213 
214 void
215 TextList::AdjustViewForContent()
216 {
217  const bool b(viewer.AdjustForContent());
218 
219  if(viewer.IsSelected() && b)
220  {
221  AdjustOffset(viewer.GetSelectedIndex() == viewer.GetHeadIndex());
222  return;
223  }
224  if(GetFullViewHeight() < GetViewPosition() + GetHeight())
225  top_offset = 0;
226  AdjustViewLength();
227 }
228 
229 void
230 TextList::AdjustViewLength()
231 {
232  const auto h(GetHeight());
233 
234  if(h != 0)
235  {
236  const auto ln_h(GetItemHeight());
237 
238  viewer.SetLength(h / ln_h + (top_offset != 0 || h % ln_h != 0));
239  }
240 }
241 
242 bool
243 TextList::CheckConfirmed(ListType::size_type idx) const
244 {
245  return viewer.IsSelected() && viewer.GetSelectedIndex() == idx;
246 }
247 
248 TextList::ListType::size_type
249 TextList::CheckPoint(SPos x, SPos y)
250 {
251  return Rect(GetSizeOf(*this)).Contains(x, y) ? (y + top_offset)
252  / GetItemHeight() + viewer.GetHeadIndex() : ListType::size_type(-1);
253 }
254 
255 void
256 TextList::InvalidateSelected(ListType::difference_type offset,
257  ListType::size_type n)
258 {
259  if(offset >= 0 && n != 0)
260  {
261  const auto ln_h(GetItemHeight());
262  Rect r(0, ln_h * offset - top_offset, GetWidth(), ln_h * n);
263 
264  if(r.Y < GetHeight())
265  {
266  r.Y = max<int>(0, r.Y);
267  RestrictUnsignedStrict(r.Height, GetHeight() - r.Y);
268  Invalidate(*this, r);
269  }
270  }
271 }
272 
273 void
274 TextList::InvalidateSelected2(ListType::difference_type x,
275  ListType::difference_type y)
276 {
277  if(y < x)
278  std::swap(x, y);
279  InvalidateSelected(x < 0 ? 0 : x, y - x + 1);
280 }
281 
282 void
283 TextList::LocateViewPosition(SDst h)
284 {
285  RestrictInInterval(h, 0, GetFullViewHeight() - GetHeight());
286 
287  if(GetViewPosition() != h)
288  {
289  const SDst item_height(GetItemHeight());
290 
291  //先保证避免部分显示的项目使视图超长,再设置视图位置。
292  AdjustViewLength();
293  viewer.SetHeadIndex(h / item_height);
294  top_offset = h % item_height;
295  //更新视图。
296  UpdateView(*this, true);
297  }
298 }
299 
300 void
301 TextList::DrawItem(const Graphics& g, const Rect& mask, const Rect& unit,
302  ListType::size_type i)
303 {
304  DrawClippedText(g, mask & (unit + Margin), tsList, GetList()[i], false);
305 }
306 
307 void
308 TextList::DrawItemBackground(const PaintContext& pc, const Rect& r)
309 {
310  FillRect<PixelType>(pc.Target.GetBufferPtr(), pc.Target.GetSize(),
311  pc.ClipArea & Rect(r.X + 1, r.Y, r.Width - 2, r.Height),
312  HilightBackColor);
313 }
314 
315 void
316 TextList::DrawItems(const PaintContext& pc)
317 {
318  const auto h(GetHeight());
319 
320  if(h != 0)
321  {
322  RefreshTextState();
323 
324  const Rect& r(pc.ClipArea);
325 
326  if(viewer.GetTotal() != 0 && bool(r))
327  {
328  const auto& g(pc.Target);
329  const auto& pt(pc.Location);
330  const auto ln_w(GetWidth());
331  const auto ln_h(GetItemHeight());
332 
333  //视图长度可能因为内容变化等原因改变,必须重新计算。
334  AdjustViewLength();
335 
336  const SPos lbound(r.Y - pt.Y);
337  const auto last(viewer.GetHeadIndex()
338  + min<ViewerType::SizeType>((lbound + r.Height + top_offset
339  - 1) / ln_h + 1, viewer.GetValid()));
340  SPos y(ln_h * ((min<SPos>(0, lbound) + top_offset - 1) / ln_h)
341  - top_offset);
342 
343  for(auto i(viewer.GetHeadIndex()); i < last; yunseq(y += ln_h, ++i))
344  {
345  SPos top(y), tmp(y + ln_h);
346 
347  RestrictInInterval<SPos>(top, 0, h);
348  RestrictInInterval<SPos>(tmp, 1, h + 1);
349  tmp -= top;
350 
351  const Rect unit(pt.X, top + pt.Y, ln_w, tmp);
352 
353  if(viewer.IsSelected() && i == viewer.GetSelectedIndex())
354  {
355  tsList.Color = HilightTextColor;
356  DrawItemBackground(pc, unit);
357  }
358  else
359  tsList.Color = ForeColor;
360  AdjustEndOfLine(tsList, unit + Margin, g.GetWidth()),
361  tsList.ResetPen(unit.GetPoint(), Margin);
362  if(y < 0)
363  tsList.Pen.Y -= top_offset;
364  DrawItem(g, pc.ClipArea, unit, i);
365  }
366  }
367  }
368 }
369 
370 void
371 TextList::Refresh(PaintEventArgs&& e)
372 {
373  DrawItems(e);
374  e.ClipArea = Rect(e.Location, GetSizeOf(*this));
375 }
376 
377 void
378 TextList::ResetView()
379 {
380  bool b(viewer.IsSelected());
381 
382  viewer.Reset();
383  if(b)
384  viewer.SetSelectedIndex(0);
385  top_offset = 0;
386  UpdateView(*this);
387 }
388 
389 void
390 TextList::SelectFirst()
391 {
392  viewer.SetSelectedIndex(0);
393  AdjustOffset(true);
394 }
395 
396 void
397 TextList::SelectLast()
398 {
399  viewer.SetSelectedIndex(GetList().size() - 1);
400  AdjustOffset(false);
401 }
402 
403 void
404 TextList::CallSelected()
405 {
406  GetSelected()(IndexEventArgs(*this, viewer.GetSelectedIndex()));
407 }
408 
409 void
410 TextList::InvokeConfirmed(ListType::size_type idx)
411 {
412  if(CheckConfirmed(idx))
413  GetConfirmed()(IndexEventArgs(*this, idx));
414 }
415 
416 
417 void
418 ResizeForContent(TextList& tl)
419 {
420  SetSizeOf(tl, Size(tl.GetMaxTextWidth() + GetHorizontalOf(tl.Margin),
421  tl.GetFullViewHeight()));
422  tl.AdjustViewLength();
423 }
424 
425 void
426 UpdateView(TextList& tl, bool is_active)
427 {
428  tl.GetViewChanged()(TextList::ViewArgs(tl, is_active));
429  tl.AdjustViewLength();
430  Invalidate(tl);
431 }
432 
434 
435 YSL_END
436