YSTest  PreAlpha_b500_20140530
The YSLib Test Project
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
DSReader.cpp
浏览该文件的文档.
1 /*
2  © 2010-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 "DSReader.h"
29 #include <algorithm> // for std::copy_n;
30 #include YFM_YSLib_UI_YWindow
31 #include YFM_YSLib_Service_TextLayout
32 
33 namespace YSLib
34 {
35 
36 using namespace Drawing;
37 using namespace Text;
38 using ystdex::next_if_eq;
39 
40 namespace
41 {
42 
47 template<typename _tBi>
48 _tBi
49 FindPreviousChar(_tBi s, _tBi b, ucs4_t c = ucs4_t())
50 {
51 // while(b < --s && *s != c)
52  while(b != --s && *s != c)
53  ;
54  return s;
55 }
56 
61 template<typename _tBi>
62 _tBi
63 FindLineFeed(const TextRegion& r, _tBi s, _tBi e)
64 {
65  const SDst wmax(r.GetWidth() - GetHorizontalOf(r.Margin));
66  SDst w(0);
67 
68 // while(s < e && *s != '\n')
69  while(s != e && *s != '\n')
70  {
71  if(std::iswprint(*s))
72  {
73  w += r.Font.GetAdvance(*s);
74  if(w >= wmax)
75  break;
76  }
77  ++s;
78  }
79  return s;
80 }
81 
86 template<typename _tBi>
87 _tBi
88 FindPreviousLineFeed(TextRegion& r, _tBi s, _tBi b)
89 {
90 // if(b < s)
91  if(b != s)
92  {
93  const auto e(s);
94  auto t(FindPreviousChar(s, b, '\n'));
95 
96  do
97  {
98  s = t;
99  if(*t == '\n')
100  ++t;
101  t = FindLineFeed(r, t, e);
102  }while(t != e);
103  }
104  return s;
105 }
106 
111 template<typename _tIn, class _tArea, class _tCon>
112 inline void
113 AdjustForNewline(_tArea& area, _tIn& i, _tCon& c)
114 {
115  using std::end;
116 
117  i = FindLineFeed(area, next_if_eq(i, '\n'), end(c));
118 }
119 
124 template<typename _tIn, class _tArea, class _tCon>
125 inline void
126 AdjustPrevious(_tArea& area, _tIn& i, _tCon& c)
127 {
128  using std::begin;
129 
130  i = FindPreviousLineFeed(area, i, begin(c));
131 }
132 
144 void
145 CopyScrollArea(YSLib::UI::BufferedTextArea& src_area,
146  size_t src_offset, YSLib::UI::BufferedTextArea& dst_area,
147  size_t dst_offset, ptrdiff_t offset, size_t n)
148 {
149  YAssert(n != 0, "Invalid number of lines found.");
150  YAssert(n <= size_t(std::abs(offset)), "Invalid offset found.");
151 
152  const SDst w(src_area.GetWidth());
153 
154  YAssert(w == dst_area.GetWidth(), "Distinct screen widths found.");
155  yunseq(src_offset *= w, dst_offset *= w, n *= w);
156  dst_area.Scroll(offset);
157  yunseq(std::copy_n(src_area.GetBufferPtr() + src_offset, n,
158  dst_area.GetBufferPtr() + dst_offset),
159  std::copy_n(src_area.GetBufferAlphaPtr() + src_offset, n,
160  dst_area.GetBufferAlphaPtr() + dst_offset));
161  src_area.Scroll(offset);
162 }
163 
169 void
170 MoveScrollArea(YSLib::UI::BufferedTextArea& area_up,
171  YSLib::UI::BufferedTextArea& area_dn, ptrdiff_t offset, size_t n)
172 {
173  YAssert(area_up.GetHeight() - area_up.Margin.Bottom - n > 0,
174  "No enough space of areas found.");
175 
176  SDst src_off(area_dn.Margin.Top),
177  dst_off(area_up.GetHeight() - area_up.Margin.Bottom - n);
178  auto* p_src(&area_dn);
179  auto* p_dst(&area_up);
180  SDst clr_off;
181 
182  if(offset > 0) //复制区域向下移动,即浏览区域向上滚动。
183  {
184  std::swap(p_src, p_dst),
185  std::swap(src_off, dst_off),
186  clr_off = area_up.Margin.Top;
187  }
188  else
189  clr_off = area_dn.GetHeight() - area_dn.Margin.Bottom - n;
190  CopyScrollArea(*p_src, src_off, *p_dst, dst_off, offset, n);
191  p_src->ClearLine(clr_off, n);
192 }
193 
195 u16
196 CheckOverRead(TextRegion& r)
197 {
198  const auto b(FetchLastLineBasePosition(r, r.GetHeight()));
199 
200  return r.Pen.Y < b ? (b - r.Pen.Y) / GetTextLineHeightExOf(r) : 0;
201 }
202 
203 } // unnamed namespace;
204 
205 namespace DS
206 {
207 
208 namespace UI
209 {
210 
211 DualScreenReader::DualScreenReader(SDst w, SDst h_up, SDst h_down,
212  FontCache& fc_)
213  : p_text(), fc(fc_), i_top(), i_btm(), overread_line_n(0), scroll_offset(0),
214  Margin(4, 4, 4, 4),
215  area_up(Rect({}, w, h_up), fc), area_dn(Rect({}, w, h_down), fc)
216 {
217  SetFontSize(),
218  SetColor(),
219  SetLineGap(),
220  area_up.Background = nullptr,
221  area_dn.Background = nullptr;
222 }
223 
224 void
226 {
227  if(area_up.LineGap != g)
228  {
229  yunseq(area_up.LineGap = g, area_dn.LineGap = g);
230  UpdateView();
231  }
232 }
233 
234 void
236 {
237  SetVisibleOf(area_up, b), SetVisibleOf(area_dn, b);
238 
239  using YSLib::UI::Invalidate;
240 
241  //强制刷新背景。
242  Invalidate(area_up);
243  Invalidate(area_dn);
244 }
245 
246 void
247 DualScreenReader::SetFont(const Font& fnt)
248 {
249  yunseq(area_up.Font = fnt, area_dn.Font = fnt);
250 }
251 void
253 {
254  area_up.Font.SetSize(s),
255  area_dn.Font.SetSize(s);
256  // NOTE: Margins shall be adjusted before output.
257 }
258 
259 void
261 {
262  AdjustForNewline(area_up, i_top, *p_text);
263 }
264 
265 void
267 {
268  AdjustPrevious(area_up, i_top, *p_text);
269 }
270 
271 void
273 {
274  yunseq(area_up.Margin = Margin, area_dn.Margin = Margin);
276  {
277  const SPos v((area_up.Margin.Bottom - area_up.Margin.Top) / 2);
278 
279  yunseq(area_up.Margin.Top += v, area_up.Margin.Bottom -= v);
280  }
281  {
282  const SPos v((area_dn.Margin.Bottom - area_dn.Margin.Top) / 2);
283 
284  yunseq(area_dn.Margin.Top += v, area_dn.Margin.Bottom -= v);
285  }
286 }
287 
288 FontSize
290 {
291  return bool(p_text) && scroll_offset != 0
292  ? ScrollByPixel(GetTextLineHeightExOf(area_up) - scroll_offset) : 0;
293 }
294 
295 void
297  YSLib::UI::Window& wnd_dn)
298 {
299  wnd_up += area_up,
300  wnd_dn += area_dn;
301 }
302 
303 void
305 {
306  using YSLib::UI::Window;
307 
308  if(const auto p_con = dynamic_cast<Window*>(FetchContainerPtr(area_up)))
309  *p_con -= area_up;
310  if(const auto p_con = dynamic_cast<Window*>(FetchContainerPtr(area_dn)))
311  *p_con -= area_dn;
312 }
313 
314 bool
316 {
317  if(YB_UNLIKELY(!p_text || p_text->GetTextSize() == 0))
318  return false;
319  if(YB_UNLIKELY(~cmd & Scroll))
320  return false;
321  if(AdjustScrollOffset() != 0)
322  return false;
323  if(cmd & Up)
324  {
325  if(YB_UNLIKELY(IsTextTop()))
326  return false;
327  }
328  else if(YB_UNLIKELY(IsTextBottom()))
329  return false;
330  YAssert(area_up.LineGap == area_dn.LineGap, "Distinct line gaps found.");
331  // TODO: Assert the fonts are same.
332  cmd &= ~Scroll;
333  if(cmd & Line)
334  {
335  const FontSize h(area_up.Font.GetHeight()), hx(h + GetLineGap());
336 
337  if(cmd & Up)
338  {
339  MoveScrollArea(area_up, area_dn, hx, h);
340  SetCurrentTextLineNOf(area_up, 0);
342  CarriageReturn(area_up);
343  PutLine(area_up, next_if_eq(i_top, '\n'), p_text->end(), '\n');
344  if(overread_line_n > 0)
345  --overread_line_n;
346  else
347  AdjustPrevious(area_up, i_btm, *p_text);
348  }
349  else
350  {
351  MoveUpForLastLine(-hx, h);
352  //注意缓冲区不保证以空字符结尾。
353  CarriageReturn(area_dn);
354  i_btm = PutLastLine();
356  }
357  Invalidate();
358  }
359  else
360  {
361  auto ln(area_up.GetTextLineNEx() + area_dn.GetTextLineNEx());
362 
363  if(cmd & Up)
364  while(ln--)
366  else
367  while(ln-- && !IsTextBottom())
368  {
369  AdjustForNewline(area_dn, i_btm, *p_text);
371  }
372  UpdateView();
373  }
374  return true;
375 }
376 
377 void
379 {
380  using YSLib::UI::Invalidate;
381 
382  //强制刷新背景。
383  Invalidate(area_up);
384  Invalidate(area_dn);
385  if(ViewChanged)
386  ViewChanged();
387 }
388 
389 void
391 {
392  YAssert(bool(p_text), "Null text buffer found.");
393 
394  const auto s(p_text->GetTextSize());
395 
396  if(s == 0)
397  {
398  Reset();
399  Invalidate();
400  return;
401  }
402  if(pos == 0)
403  i_top = p_text->begin();
404  else if(pos < s)
405  {
406  i_top = p_text->GetIterator(pos);
407  ++i_top;
409  }
410  else
411  return;
412  UpdateView();
413 }
414 
415 void
417 {
418  if(YB_LIKELY(file))
419  {
420  p_text = make_unique<Text::TextFileBuffer>(file);
421  yunseq(i_top = p_text->begin(), i_btm = p_text->end());
422  UpdateView();
423  }
424  else
425  {
426  UnloadText();
427  Reset();
428  PutString(area_up, u"文件打开失败!");
429  Invalidate();
430  }
431 }
432 
433 void
434 DualScreenReader::MoveUpForLastLine(ptrdiff_t off, size_t h)
435 {
436  MoveScrollArea(area_up, area_dn, off, h);
437 
438  u16 n(area_dn.GetTextLineNEx());
439 
440  YAssert(n != 0, "No Enough height.");
441  SetCurrentTextLineNOf(area_dn, --n);
442 }
443 
446 {
447  return PutLine(area_dn, next_if_eq(i_btm, '\n'), p_text->end(), '\n');
448 }
449 
450 void
452 {
453  //清除字符区域缓冲区。
454  area_up.ClearImage();
455  area_dn.ClearImage();
456  //根据行距调整并均衡边距。
457  AdjustMargins();
458  //复位缓存区域写入位置。
459  area_up.ResetPen();
460  area_dn.ResetPen();
461 }
462 
463 FontSize
465 {
466  const FontSize ln_h_ex(GetTextLineHeightExOf(area_up));
467 
468  YAssert(scroll_offset < ln_h_ex, "Invalid scroll offset found."),
469  YAssert(bool(p_text), "Null text buffer found.");
470 
471  if(YB_UNLIKELY(i_btm == p_text->end() || scroll_offset + h > ln_h_ex))
472  return 0;
473  MoveUpForLastLine(-h, h);
474  //注意缓冲区不保证以空字符结尾。
475  CarriageReturn(area_dn);
476  if(YB_LIKELY((scroll_offset += h) < ln_h_ex))
477  {
478  area_dn.Pen.Y += ln_h_ex - scroll_offset;
479  PutLastLine();
480  }
481  else
482  {
483  i_btm = PutLastLine();
485  scroll_offset = 0;
486  }
487  Invalidate();
488  return h;
489 }
490 
491 void
493 {
494  RestrictInClosedInterval<SDst>(h, 0, MainScreenHeight - 40);
495  h = MainScreenHeight - h;
496 
497  const SDst w(area_dn.GetWidth());
498 
499  SetSizeOf(area_dn, Size(w, h)),
500  area_dn.SetSize(w, h);
501  UpdateView();
502 }
503 
504 void
506 {
509  p_text = nullptr);
510 }
511 
512 void
514 {
515  if(YB_UNLIKELY(!p_text || p_text->GetTextSize() == 0))
516  return;
517  Reset();
518  {
519  auto i_new(PutString(area_up, next_if_eq(i_top, '\n'),
520  p_text->end()));
521 
522  if(YB_UNLIKELY(i_new == p_text->end()))
523  {
524  i_btm = i_new;
525  overread_line_n = CheckOverRead(area_up) + area_dn.GetTextLineNEx();
526  }
527  else
528  {
529  i_btm = PutString(area_dn, i_new, p_text->end());
530  overread_line_n = CheckOverRead(area_dn);
531  }
532  }
533  if(YB_LIKELY(!IsTextBottom() && *i_btm == '\n'))
534  --i_btm;
535  Invalidate();
536 }
537 
538 } // namespace UI;
539 
540 } // namespace DS;
541 
542 } // namespace YSLib;
543 
Point Pen
笔坐标。
Definition: TextBase.h:99
u8 LineGap
行距。
Definition: TextBase.h:100
Drawing::FontSize ScrollByPixel(Drawing::FontSize)
向下滚屏指定像素行。
Definition: DSReader.cpp:464
目标编码迭代器类型。
Definition: textmgr.h:68
Drawing::FontSize AdjustScrollOffset()
调整滚屏像素偏移量:立即继续滚动至下一整文本行。
Definition: DSReader.cpp:289
Drawing::FontSize scroll_offset
滚屏像素偏移量。
Definition: DSReader.h:110
Text::TextFileBuffer::iterator i_top
文本区域输入迭代器。
Definition: DSReader.h:96
YF_API void Invalidate(IWidget &, const Rect &)
无效化:使相对于部件的指定区域在直接和间接的窗口缓冲区中无效。
Definition: ywidget.cpp:111
文本文件类。
Definition: TextFile.h:56
void SetVisible(bool=true)
设置文本区域可见性。
Definition: DSReader.cpp:235
窗口。
Definition: ywindow.h:44
YF_API void SetSizeOf(IWidget &, const Size &)
设置部件大小。
Definition: ywidget.cpp:83
std::uint16_t SDst
屏幕坐标距离。
Definition: Video.h:39
std::int16_t SPos
屏幕坐标度量。
Definition: Video.h:38
SDst AdjustBottomMarginOf(TextRegion &tr)
按字体高度和行距调整文本区域的底边距。
Definition: TextLayout.h:84
YF_API SPos FetchLastLineBasePosition(const TextState &, SDst)
取指定文本状态在指定高的区域中表示的最底行的基线位置(纵坐标)。
Definition: TextLayout.cpp:60
适用于 DS 的双屏阅读器。
Padding Margin
边距:文本区域到显示区域的距离。
Definition: TextBase.h:94
YSLib::UI::BufferedTextArea area_up
上下屏幕对应文本区域。
Definition: DSReader.h:127
窗口背景。
Definition: ystyle.h:182
void swap(any &x, any &y)
交换对象。
Definition: any.h:729
#define YB_UNLIKELY(expr)
分支预测提示。
Definition: ydef.h:298
void UpdateView()
更新视图。
Definition: DSReader.cpp:513
HBrush Background
背景。
Definition: ywidget.h:374
#define yunseq
无序列依赖表达式组求值。
Definition: ydef.h:748
void Attach(YSLib::UI::Window &, YSLib::UI::Window &)
附加到窗口。
Definition: DSReader.cpp:296
字体缓存。
Definition: Font.h:415
屏幕标准矩形:表示屏幕矩形区域。
Definition: ygdibase.h:416
Drawing::Font Font
字体。
Definition: TextBase.h:57
Text::TextFileBuffer::iterator PutLastLine()
Definition: DSReader.cpp:445
YSLib::UI::BufferedTextArea area_dn
Definition: DSReader.h:128
void Reset()
复位输出显示状态。
Definition: DSReader.cpp:451
缓冲文本区域。
Definition: textarea.h:74
Drawing::Padding Margin
公用边距。
Definition: DSReader.h:119
void MoveUpForLastLine(ptrdiff_t, size_t)
Definition: DSReader.cpp:434
bool Execute(Command)
执行阅读器命令。
Definition: DSReader.cpp:315
void SetCurrentTextLineNOf(TextState &ts, u16 n)
Definition: TextBase.cpp:74
_tIter PutLine(_tRenderer &r, _tIter s)
打印迭代器指定的起始字符的字符串,直至行尾或字符迭代终止。
Definition: TextRenderer.h:110
unique_ptr< Text::TextFileBuffer > p_text
文本资源。
Definition: DSReader.h:86
void Locate(size_t)
文本定位。
Definition: DSReader.cpp:390
void SetLineGap(u8=0)
设置行距。
Definition: DSReader.cpp:225
_tIter PutString(_tRenderer &r, _tIter s)
打印迭代器指定的起始字符的字符串,直至区域末尾或字符迭代终止。
Definition: TextRenderer.h:223
std::uint8_t u8
通用数据类型。
Definition: yadaptor.h:67
void Detach()
从窗口分离。
Definition: DSReader.cpp:304
char32_t ucs4_t
UCS-4 字符类型。
Definition: chrdef.h:45
bounds & r
Definition: ydraw.h:220
void Invalidate()
无效化文本区域,并调用 ViewChanged (仅当非空)。
Definition: DSReader.cpp:378
c yconstfn g
Definition: ystyle.h:104
#define YB_LIKELY(expr)
Definition: ydef.h:297
字体:字模,包含字型、样式和大小。
Definition: Font.h:546
std::function< void()> ViewChanged
视图变更回调函数。
Definition: DSReader.h:136
void AdjustMargins()
调整边距:使用公用边距更新各文本显示区域的边距。
Definition: DSReader.cpp:272
std::uint16_t u16
Definition: yadaptor.h:68
Text::TextFileBuffer::iterator i_btm
Definition: DSReader.h:97
FontSize GetHeight() const ynothrow
取字体对应的字符高度。
Definition: Font.cpp:527
屏幕区域大小。
Definition: ygdibase.h:249
void Scroll(ptrdiff_t n)
缓冲区特效:整体移动 n 像素。
_tIn next_if_eq(_tIn i, const _type &val, typename std::iterator_traits< _tIn >::difference_type n=1)
Definition: iterator.hpp:107
void UnloadText()
卸载文本。
Definition: DSReader.cpp:505
byte v
PDefHOp(TextState &,=, const PenStyle &ps) ImplRet(PenStyle void ResetPen()
赋值:笔样式。
Definition: TextBase.h:145
void SetFontSize(Drawing::FontSize=Drawing::Font::DefaultSize)
设置文本区域的字体大小。
Definition: DSReader.cpp:252
u16 overread_line_n
读入文件结束后的空行数。
Definition: DSReader.h:105
#define YAssert(_expr, _msg)
Definition: cassert.h:73
void Stretch(SDst)
伸缩:从最大值起向上调整(减少)下文字区域的高后更新视图。
Definition: DSReader.cpp:492
void LoadText(TextFile &)
载入文本。
Definition: DSReader.cpp:416
u8 FontSize
字体大小。
Definition: Font.h:63