YSTest: textlist.cpp 源文件

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){
134 InvokeConfirmed(CheckPoint(e));
135 },
136 FetchEvent<Paint>(*this).Add(BorderBrush(), BoundaryPriority),
137 FetchEvent<GotFocus>(*this) += invalidator,
138 FetchEvent<LostFocus>(*this) += invalidator
139 );
140 AdjustViewLength();
141 }
142
143 SDst
144 TextList::GetFullViewHeight() const
145 {
146 return GetItemHeight() * viewer.GetTotal();
147 }
148 SDst
149 TextList::GetViewPosition() const
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 {
159 MTextList::SetList(h);
160 viewer.SetContainer(*h);
161 AdjustViewLength();
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
180 TextList::SetSelected(SPos x, SPos y)
181 {
182 SetSelected(CheckPoint(x, y));
183 }
184
185 SDst
186 TextList::AdjustOffset(bool is_top)
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;
197 AdjustViewLength();
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;
210 AdjustViewLength();
211 viewer.IncreaseHead(tmp / item_height);
212 }
213 return d;
214 }
215 }
216 return 0;
217 }
218
219 void
220 TextList::AdjustViewForContent()
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;
231 AdjustViewLength();
232 }
233
234 void
235 TextList::AdjustViewLength()
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
254 TextList::CheckPoint(SPos x, SPos y)
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
288 TextList::LocateViewPosition(SDst h)
289 {
290 RestrictInInterval(h, 0, GetFullViewHeight() - GetHeight());
291
292 if(GetViewPosition() != h)
293 {
294 const SDst item_height(GetItemHeight());
295
296 //先保证避免部分显示的项目使视图超长，再设置视图位置。
297 AdjustViewLength();
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 //视图长度可能因为内容变化等原因改变，必须重新计算。
340 AdjustViewLength();
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
377 TextList::Refresh(PaintEventArgs&& e)
378 {
379 DrawItems(e);
380 }
381
382 void
383 TextList::ResetView()
384 {
385 bool b(viewer.IsSelected());
386
387 viewer.Reset();
388 if(b)
389 viewer.SetSelectedIndex(0);
390 top_offset = 0;
391 UpdateView(*this);
392 }
393
394 void
395 TextList::SelectFirst()
396 {
397 viewer.SetSelectedIndex(0);
398 AdjustOffset(true);
399 }
400
401 void
402 TextList::SelectLast()
403 {
404 viewer.SetSelectedIndex(GetList().size() - 1);
405 AdjustOffset(false);
406 }
407
408 void
409 TextList::CallSelected()
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
YSLib::UI::InvalidateSelected
void InvalidateSelected(ListType::difference_type offset, ListType::size_type diff=1)
无效化偏移量对应的列表项区域。
Definition: textlist.cpp:261
YSLib::UI::GSequenceViewer::DifferenceType
typename _tCon::difference_type DifferenceType
项目索引差值类型。
Definition: viewer.hpp:56
YSLib::UI::ClearSelected
ClearSelected
Definition: textlist.h:229
YSLib::UI::viewer
ViewerType viewer
列表视图。
Definition: textlist.h:86
YSLib::UI::GetViewPosition
SDst GetViewPosition() const
取视图顶端竖直位置。
Definition: textlist.cpp:149
YSLib::UI::GetSizeOf
pt pt Y const IWidget &wgt const IWidget &wgt GetSizeOf
无效化：使相对于部件的子部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: ywidget.h:156
YSLib::UI::Refresh
void Refresh(PaintEventArgs &&) override
刷新：按指定参数绘制界面并更新状态。
YSLib::UI::OnKeyHeld
YF_API void OnKeyHeld(KeyEventArgs &&)
处理键接触保持事件。
Definition: ycontrol.cpp:108
YSLib::UI::IndexEventArgs
GValueEventArgs< MTextList::IndexType > IndexEventArgs
索引事件。
Definition: textlist.h:47
YModules.h
YSLib::UI::AdjustOffset
SDst AdjustOffset(bool)
调整列表视图底项目的竖直偏移量为零。
Definition: textlist.cpp:186
YSLib::UI::Invalidate
YF_API void Invalidate(IWidget &, const Rect &)
无效化：使相对于部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: ywidget.cpp:111
YSLib::Drawing::DrawClippedText
YF_API void DrawClippedText(const Graphics &g, const Rect &mask, TextState &ts, const String &str, bool line_wrap)
绘制剪切区域的文本。
Definition: TextRenderer.cpp:214
YSLib::UI::PaintEventArgs
部件绘制参数。
Definition: ywgtevt.h:276
YSLib::UI::UpdateView
void UpdateView(TextList &tl, bool is_active)
Definition: textlist.cpp:431
YSLib::UI::GSequenceViewer::SetSelectedIndex
bool SetSelectedIndex(SizeType t)
设置选中项目的索引。
Definition: viewer.hpp:171
YSLib::UI::InvalidateSelected2
void InvalidateSelected2(ListType::difference_type, ListType::difference_type)
无效化偏移量对应的列表项区域。
Definition: textlist.cpp:279
YSLib::Drawing::Size::Height
SDst Height
宽和高。
Definition: ygdibase.h:258
YSLib::UI::SelectLast
void SelectLast()
选择最后一个项目。
Definition: textlist.cpp:402
YSLib::UI::SetSizeOf
YF_API void SetSizeOf(IWidget &, const Size &)
设置部件大小。
Definition: ywidget.cpp:83
YSLib::UI::GSequenceViewer::SetHeadIndex
bool SetHeadIndex(SizeType t)
设置视图中首个项目的索引。
Definition: viewer.hpp:140
YSLib::UI::BoundaryPriority
yconstexpr EventPriority BoundaryPriority(0x60)
YSLib::UI::MTextList::SetList
void SetList(const shared_ptr< ListType > &h)
设置文本列表。
Definition: label.h:215
YSLib::UI::ResetView
void ResetView()
复位视图。
platform::SDst
std::uint16_t SDst
屏幕坐标距离。
Definition: Video.h:39
platform::SPos
std::int16_t SPos
屏幕坐标度量。
Definition: Video.h:38
YSLib::UI::CheckConfirmed
virtual bool CheckConfirmed(ListType::size_type) const
检查列表中的指定项是否有效。
ystdex::swap
void swap(any &x, any &y)
交换对象。
Definition: any.h:729
YSLib::UI::DrawItemBackground
virtual void DrawItemBackground(const PaintContext &, const Rect &r)
描画列表项背景。
Definition: textlist.cpp:314
YSLib::UI::CyclicTraverse
bool CyclicTraverse
循环选择遍历。
Definition: textlist.h:83
YSLib::UI::SetSelected
void SetSelected(ListType::size_type)
按指定项目索引设置选中项目。
YSLib::UI::SetList
void SetList(const shared_ptr< ListType > &)
设置文本列表。
yunseq
#define yunseq
无序列依赖表达式组求值。
Definition: ydef.h:748
YSLib::Drawing::Rect::Contains
bool Contains(int px, int py) const ynothrow
判断点 (px, py) 是否在矩形内或边上。
Definition: ygdibase.cpp:80
YSLib::UI::SelectFirst
void SelectFirst()
选择第一个项目。
Definition: textlist.cpp:395
YSLib::Drawing::GBinaryGroup::Y
_type Y
分量。
Definition: ygdibase.h:59
YSLib::RestrictUnsignedStrict
void RestrictUnsignedStrict(_type &u, unsigned b) ynothrow
约束无符号整数 u 在区间上界 b 内。
Definition: ycutil.h:299
YSLib::Drawing::Rect
屏幕标准矩形：表示屏幕矩形区域。
Definition: ygdibase.h:416
YSLib::UI::LocateViewPosition
void LocateViewPosition(SDst)
定位视图顶端至指定竖直位置。
YSLib::UI::GSequenceViewer::AdjustForContent
bool AdjustForContent()
按序列内容大小依次调整选中和首个项目的索引。
Definition: viewer.hpp:191
YSLib::UI::DrawItem
virtual void DrawItem(const Graphics &, const Rect &mask, const Rect &, ListType::size_type)
绘制列表项。
YSLib::Drawing::GBinaryGroup::X
_type X
Definition: ygdibase.h:59
YSLib::UI::AdjustViewForContent
void AdjustViewForContent()
按内容大小依次调整视图中选中和首个项目的索引，然后按需调整竖直偏移量。
YSLib::UI::top_offset
SDst top_offset
列表视图首项目超出上边界的竖直偏移量。
Definition: textlist.h:87
YSLib::RestrictInInterval
void RestrictInInterval(_type &i, int a, int b) ynothrow
约束整数 i 在左闭右开区间 [a, b) 中。
Definition: ycutil.h:283
YSLib::Drawing::r
bounds & r
Definition: ydraw.h:220
YSLib::UI::CheckPoint
ListType::size_type CheckPoint(SPos, SPos)
检查点（相对于所在缓冲区的控件坐标）是否在选择范围内，
Definition: textlist.cpp:254
YSLib::UI::InvokeConfirmed
void InvokeConfirmed(ListType::size_type)
检查和调用确认事件处理器。
Definition: textlist.cpp:415
YSLib::Drawing::g
c yconstfn g
Definition: ystyle.h:104
YSLib::UI::AdjustViewLength
void AdjustViewLength()
调整视图长度。
YSLib::UI::CallSelected
void CallSelected()
调用选中事件处理器。
Definition: textlist.cpp:409
YSLib::UI::ResizeForContent
void ResizeForContent(TextList &tl)
Definition: textlist.cpp:423
YSLib::UI::GSequenceViewer::SetLength
bool SetLength(SizeType l)
设置长度。
Definition: viewer.hpp:158
YSLib::Drawing::Size
屏幕区域大小。
Definition: ygdibase.h:249
YSLib::UI::DrawItems
virtual void DrawItems(const PaintContext &)
绘制列表。
Definition: textlist.cpp:322
YSLib::UI::GSequenceViewer::Contains
int int is_selected bool Contains(SizeType i) const
判断是否在有效范围内包含指定项目索引。
Definition: viewer.hpp:108
YSLib::UI::Selected
Selected const shared_ptr< ListType > const pair< Color, Color > Selected
Definition: textlist.h:122
YSLib::Drawing::Size::Width
SDst Width
Definition: ygdibase.h:258
YSLib::UI::ViewArgs
GValueEventArgs< bool > ViewArgs
视图参数类型。
Definition: textlist.h:67