YSTest: textlist.cpp 源文件

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"
32 #include "YSLib/Service/TextLayout.h"
33
34 YSL_BEGIN
35
36 using namespace Drawing;
37
38 YSL_BEGIN_NAMESPACE(UI)
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
433 YSL_END_NAMESPACE(UI)
434
435 YSL_END
436