YSTest  PreAlpha_b400_20130424
The YSLib Test Project
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
DSReader.cpp
浏览该文件的文档.
1 /*
2  Copyright by FrankHB 2010 - 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 "DSReader.h"
29 #include <algorithm> // for std::copy_n;
30 #include <YSLib/UI/ywindow.h>
32 
34 
35 using namespace Drawing;
36 using namespace Text;
37 using ystdex::next_if_eq;
38 
39 namespace
40 {
41 
46 template<typename _tBi>
47 _tBi
48 FindPreviousChar(_tBi s, _tBi b, ucs4_t c = ucs4_t())
49 {
50 // while(b < --s && *s != c)
51  while(b != --s && *s != c)
52  ;
53  return s;
54 }
55 
60 template<typename _tBi>
61 _tBi
62 FindLineFeed(const TextRegion& r, _tBi s, _tBi e)
63 {
64  const SDst wmax(r.GetWidth() - GetHorizontalOf(r.Margin));
65  SDst w(0);
66 
67 // while(s < e && *s != '\n')
68  while(s != e && *s != '\n')
69  {
70  if(std::iswprint(*s))
71  {
72  w += r.Font.GetAdvance(*s);
73  if(w >= wmax)
74  break;
75  }
76  ++s;
77  }
78  return s;
79 }
80 
85 template<typename _tBi>
86 _tBi
87 FindPreviousLineFeed(TextRegion& r, _tBi s, _tBi b)
88 {
89 // if(b < s)
90  if(b != s)
91  {
92  const auto e(s);
93  auto t(FindPreviousChar(s, b, '\n'));
94 
95  do
96  {
97  s = t;
98  if(*t == '\n')
99  ++t;
100  t = FindLineFeed(r, t, e);
101  }while(t != e);
102  }
103  return s;
104 }
105 
110 template<typename _tIn, class _tArea, class _tContainer>
111 inline void
112 AdjustForNewline(_tArea& area, _tIn& i, _tContainer& c)
113 {
114  i = FindLineFeed(area, next_if_eq(i, '\n'), c.GetEnd());
115 }
116 
121 template<typename _tIn, class _tArea, class _tContainer>
122 inline void
123 AdjustPrevious(_tArea& area, _tIn& i, _tContainer& c)
124 {
125  i = FindPreviousLineFeed(area, i, c.GetBegin());
126 }
127 
139 void
140 CopyScrollArea(YSLib::UI::BufferedTextArea& src_area,
141  size_t src_offset, YSLib::UI::BufferedTextArea& dst_area,
142  size_t dst_offset, ptrdiff_t offset, size_t n)
143 {
144  YAssert(n != 0, "Invalid number of lines found.");
145  YAssert(n <= size_t(std::abs(offset)), "Invalid offset found.");
146 
147  const SDst w(src_area.GetWidth());
148 
149  YAssert(w == dst_area.GetWidth(), "Distinct screen widths found.");
150 
151  yunseq(src_offset *= w, dst_offset *= w, n *= w);
152  dst_area.Scroll(offset);
153  yunseq(std::copy_n(src_area.GetBufferPtr() + src_offset, n,
154  dst_area.GetBufferPtr() + dst_offset), std::copy_n(
155  src_area.GetBufferAlphaPtr() + src_offset, n,
156  dst_area.GetBufferAlphaPtr() + dst_offset));
157  src_area.Scroll(offset);
158 }
159 
165 void
166 MoveScrollArea(YSLib::UI::BufferedTextArea& area_up,
167  YSLib::UI::BufferedTextArea& area_dn, ptrdiff_t offset, size_t n)
168 {
169  YAssert(area_up.GetHeight() - area_up.Margin.Bottom - n > 0,
170  "No enough space of areas found.");
171 
172  SDst src_off(area_dn.Margin.Top),
173  dst_off(area_up.GetHeight() - area_up.Margin.Bottom - n);
174  auto* p_src(&area_dn);
175  auto* p_dst(&area_up);
176  SDst clr_off;
177 
178  if(offset > 0) //复制区域向下移动,即浏览区域向上滚动。
179  {
180  std::swap(p_src, p_dst),
181  std::swap(src_off, dst_off),
182  clr_off = area_up.Margin.Top;
183  }
184  else
185  clr_off = area_dn.GetHeight() - area_dn.Margin.Bottom - n;
186  CopyScrollArea(*p_src, src_off, *p_dst, dst_off, offset, n);
187  p_src->ClearLine(clr_off, n);
188 }
189 
191 u16
192 CheckOverRead(TextRegion& r)
193 {
194  const auto b(FetchLastLineBasePosition(r, r.GetHeight()));
195 
196  return r.Pen.Y < b ? (b - r.Pen.Y) / GetTextLineHeightExOf(r) : 0;
197 }
198 
199 } // unnamed namespace;
200 
202 
204 
206  FontCache& fc_)
207  : p_text(), fc(fc_), i_top(), i_btm(), overread_line_n(0), scroll_offset(0),
208  Margin(4, 4, 4, 4),
209  area_up(Rect({}, w, h_up), fc), area_dn(Rect({}, w, h_down), fc)
210 {
211  SetFontSize(),
212  SetColor(),
213  SetLineGap(),
214  area_up.Background = nullptr,
215  area_dn.Background = nullptr;
216 }
217 
218 void
220 {
221  if(area_up.LineGap != g)
222  {
223  yunseq(area_up.LineGap = g, area_dn.LineGap = g);
224  UpdateView();
225  }
226 }
227 
228 void
230 {
231  SetVisibleOf(area_up, b), SetVisibleOf(area_dn, b);
232 
233  using YSLib::UI::Invalidate;
234 
235  //强制刷新背景。
236  Invalidate(area_up);
237  Invalidate(area_dn);
238 }
239 
240 void
241 DualScreenReader::SetFont(const Font& fnt)
242 {
243  yunseq(area_up.Font = fnt, area_dn.Font = fnt);
244 }
245 void
247 {
248  area_up.Font.SetSize(s),
249  area_dn.Font.SetSize(s);
250  // NOTE: Margins shall be adjusted before output.
251 }
252 
253 void
255 {
256  AdjustForNewline(area_up, i_top, *p_text);
257 }
258 
259 void
261 {
262  AdjustPrevious(area_up, i_top, *p_text);
263 }
264 
265 void
267 {
268  yunseq(area_up.Margin = Margin, area_dn.Margin = Margin);
270  {
271  const SPos v((area_up.Margin.Bottom - area_up.Margin.Top) / 2);
272 
273  yunseq(area_up.Margin.Top += v, area_up.Margin.Bottom -= v);
274  }
275  {
276  const SPos v((area_dn.Margin.Bottom - area_dn.Margin.Top) / 2);
277 
278  yunseq(area_dn.Margin.Top += v, area_dn.Margin.Bottom -= v);
279  }
280 }
281 
282 FontSize
284 {
285  return bool(p_text) && scroll_offset != 0
286  ? ScrollByPixel(GetTextLineHeightExOf(area_up) - scroll_offset) : 0;
287 }
288 
289 void
291  YSLib::UI::Window& wnd_dn)
292 {
293  wnd_up += area_up,
294  wnd_dn += area_dn;
295 }
296 
297 void
299 {
300  using YSLib::UI::Window;
301 
302  if(auto pCon = dynamic_cast<Window*>(FetchContainerPtr(area_up)))
303  *pCon -= area_up;
304  if(auto pCon = dynamic_cast<Window*>(FetchContainerPtr(area_dn)))
305  *pCon -= area_dn;
306 }
307 
308 bool
310 {
311  if(YB_UNLIKELY(!p_text || p_text->GetTextSize() == 0))
312  return false;
313  if(YB_UNLIKELY(~cmd & Scroll))
314  return false;
315  if(AdjustScrollOffset() != 0)
316  return false;
317  if(cmd & Up)
318  {
319  if(YB_UNLIKELY(IsTextTop()))
320  return false;
321  }
322  else if(YB_UNLIKELY(IsTextBottom()))
323  return false;
324 
325  YAssert(area_up.LineGap == area_dn.LineGap, "Distinct line gaps found.");
326  // TODO: Assert the fonts are same.
327 
328  cmd &= ~Scroll;
329  if(cmd & Line)
330  {
331  const FontSize h(area_up.Font.GetHeight()), hx(h + GetLineGap());
332 
333  if(cmd & Up)
334  {
335  MoveScrollArea(area_up, area_dn, hx, h);
336  SetCurrentTextLineNOf(area_up, 0);
337  AdjustForPrevNewline();
338  CarriageReturn(area_up);
339  PutLine(area_up, next_if_eq(i_top, '\n'), p_text->GetEnd(), '\n');
340  if(overread_line_n > 0)
341  --overread_line_n;
342  else
343  AdjustPrevious(area_up, i_btm, *p_text);
344  }
345  else
346  {
347  MoveUpForLastLine(-hx, h);
348  //注意缓冲区不保证以空字符结尾。
349  CarriageReturn(area_dn);
350  i_btm = PutLastLine();
351  AdjustForFirstNewline();
352  }
353  Invalidate();
354  }
355  else
356  {
357  auto ln(area_up.GetTextLineNEx() + area_dn.GetTextLineNEx());
358 
359  if(cmd & Up)
360  while(ln--)
361  AdjustForPrevNewline();
362  else
363  while(ln-- && !IsTextBottom())
364  {
365  AdjustForNewline(area_dn, i_btm, *p_text);
366  AdjustForFirstNewline();
367  }
368  UpdateView();
369  }
370  return true;
371 }
372 
373 void
375 {
376  using YSLib::UI::Invalidate;
377 
378  //强制刷新背景。
379  Invalidate(area_up);
380  Invalidate(area_dn);
381  if(ViewChanged)
382  ViewChanged();
383 }
384 
385 void
387 {
388  YAssert(bool(p_text), "Null text buffer found.");
389 
390  const auto s(p_text->GetTextSize());
391 
392  if(s == 0)
393  {
394  Reset();
395  Invalidate();
396  return;
397  }
398  if(pos == 0)
399  i_top = p_text->GetBegin();
400  else if(pos < s)
401  {
402  i_top = p_text->GetIterator(pos);
403  ++i_top;
404  AdjustForPrevNewline();
405  }
406  else
407  return;
408  UpdateView();
409 }
410 
411 void
413 {
414  if(YB_LIKELY(file))
415  {
416  p_text = make_unique<Text::TextFileBuffer>(file);
417  yunseq(i_top = p_text->GetBegin(), i_btm = p_text->GetEnd());
418  UpdateView();
419  }
420  else
421  {
422  UnloadText();
423  Reset();
424  PutString(area_up, u"文件打开失败!");
425  Invalidate();
426  }
427 }
428 
429 void
431 {
432  MoveScrollArea(area_up, area_dn, off, h);
433 
434  u16 n(area_dn.GetTextLineNEx());
435 
436  YAssert(n != 0, "No Enough height.");
437 
438  SetCurrentTextLineNOf(area_dn, --n);
439 }
440 
441 Text::TextFileBuffer::Iterator
443 {
444  return PutLine(area_dn, next_if_eq(i_btm, '\n'), p_text->GetEnd(), '\n');
445 }
446 
447 void
449 {
450  //清除字符区域缓冲区。
451  area_up.ClearImage();
452  area_dn.ClearImage();
453  //根据行距调整并均衡边距。
454  AdjustMargins();
455  //复位缓存区域写入位置。
456  area_up.ResetPen();
457  area_dn.ResetPen();
458 }
459 
460 FontSize
462 {
463  const FontSize ln_h_ex(GetTextLineHeightExOf(area_up));
464 
465  YAssert(scroll_offset < ln_h_ex, "Invalid scroll offset found."),
466  YAssert(bool(p_text), "Null text buffer found.");
467 
468  if(YB_UNLIKELY(i_btm == p_text->GetEnd() || scroll_offset + h > ln_h_ex))
469  return 0;
470  MoveUpForLastLine(-h, h);
471  //注意缓冲区不保证以空字符结尾。
472  CarriageReturn(area_dn);
473  if(YB_LIKELY((scroll_offset += h) < ln_h_ex))
474  {
475  area_dn.Pen.Y += ln_h_ex - scroll_offset;
476  PutLastLine();
477  }
478  else
479  {
480  i_btm = PutLastLine();
481  AdjustForFirstNewline();
482  scroll_offset = 0;
483  }
484  Invalidate();
485  return h;
486 }
487 
488 void
490 {
492  h = MainScreenHeight - h;
493 
494  const SDst w(area_dn.GetWidth());
495 
496  SetSizeOf(area_dn, Size(w, h)),
497  area_dn.SetSize(w, h);
498  UpdateView();
499 }
500 
501 void
503 {
504  yunseq(i_top = Text::TextFileBuffer::Iterator(),
505  i_btm = Text::TextFileBuffer::Iterator(),
506  p_text = nullptr);
507 }
508 
509 void
511 {
512  if(YB_UNLIKELY(!p_text || p_text->GetTextSize() == 0))
513  return;
514  Reset();
515  {
516  auto i_new(PutString(area_up, next_if_eq(i_top, '\n'),
517  p_text->GetEnd()));
518 
519  if(YB_UNLIKELY(i_new == p_text->GetEnd()))
520  {
521  i_btm = i_new;
522  overread_line_n = CheckOverRead(area_up) + area_dn.GetTextLineNEx();
523  }
524  else
525  {
526  i_btm = PutString(area_dn, i_new, p_text->GetEnd());
527  overread_line_n = CheckOverRead(area_dn);
528  }
529  }
530  if(YB_LIKELY(!IsTextBottom() && *i_btm == '\n'))
531  --i_btm;
532  Invalidate();
533 }
534 
536 
538 
539 YSL_END
540