Numworks Epsilon  1.4.1
Graphing Calculator Operating System
scroll_view.cpp
Go to the documentation of this file.
1 #include <escher/scroll_view.h>
2 #include <escher/palette.h>
3 #include <escher/metric.h>
4 
5 extern "C" {
6 #include <assert.h>
7 }
8 
9 ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) :
10  View(),
11  m_contentView(contentView),
12  m_dataSource(dataSource),
13  m_verticalScrollIndicator(ScrollViewIndicator::Direction::Vertical),
14  m_horizontalScrollIndicator(ScrollViewIndicator::Direction::Horizontal),
15  m_topMargin(0),
16  m_rightMargin(0),
17  m_bottomMargin(0),
18  m_leftMargin(0),
19  m_indicatorThickness(20),
20  m_showsIndicators(true),
21  m_colorsBackground(true),
22  m_backgroundColor(Palette::WallScreen)
23 {
24  assert(m_dataSource != nullptr);
25 }
26 
32 }
33 
34 bool ScrollView::hasVerticalIndicator() const {
35  if (m_showsIndicators) {
36  return m_verticalScrollIndicator.end() < 1 || m_verticalScrollIndicator.start() > 0;
37  }
38  return false;
39 }
40 
41 bool ScrollView::hasHorizontalIndicator() const {
42  if (m_showsIndicators) {
43  return m_horizontalScrollIndicator.end() < 1 || m_horizontalScrollIndicator.start() > 0;
44  }
45  return false;
46 }
47 
48 int ScrollView::numberOfSubviews() const {
49  return 1 + hasVerticalIndicator() + hasHorizontalIndicator();
50 }
51 
52 View * ScrollView::subviewAtIndex(int index) {
53  switch (index) {
54  case 0:
55  return m_contentView;
56  case 1:
57  return hasHorizontalIndicator() ? &m_horizontalScrollIndicator : &m_verticalScrollIndicator;
58  case 2:
59  return &m_verticalScrollIndicator;
60  }
61  return nullptr;
62 }
63 
64 void ScrollView::drawRect(KDContext * ctx, KDRect rect) const {
65  if (!m_colorsBackground) {
66  return;
67  }
68  KDCoordinate height = bounds().height();
69  KDCoordinate width = bounds().width();
70  KDCoordinate offsetX = contentOffset().x();
71  KDCoordinate offsetY = contentOffset().y();
72  KDCoordinate contentHeight = m_contentView->bounds().height();
73  KDCoordinate contentWidth = m_contentView->bounds().width();
74  ctx->fillRect(KDRect(0, 0, width, m_topMargin-offsetY), m_backgroundColor);
75  ctx->fillRect(KDRect(0, contentHeight+m_topMargin-offsetY, width, height - contentHeight - m_topMargin + offsetY), m_backgroundColor);
76  ctx->fillRect(KDRect(0, 0, m_leftMargin-offsetX, height), m_backgroundColor);
77  ctx->fillRect(KDRect(contentWidth + m_leftMargin - offsetX, 0, width - contentWidth - m_leftMargin + offsetX, height), m_backgroundColor);
78 }
79 
80 void ScrollView::scrollToContentPoint(KDPoint p, bool allowOverscroll) {
81  if (!allowOverscroll && !m_contentView->bounds().contains(p)) {
82  return;
83  }
84  KDCoordinate offsetX = 0;
85  KDCoordinate offsetY = 0;
86  KDRect visibleRect = visibleContentRect();
87  if (visibleRect.left() > p.x()) {
88  offsetX = p.x() - visibleRect.left();
89  }
90  if (visibleRect.right() < p.x()) {
91  offsetX = p.x() - visibleRect.right();
92  }
93  if (visibleRect.top() > p.y()) {
94  offsetY = p.y() - visibleRect.top();
95  }
96  if (visibleRect.bottom() < p.y()) {
97  offsetY = p.y() - visibleRect.bottom();
98  }
99  if (offsetX != 0 || offsetY != 0) {
100  setContentOffset(contentOffset().translatedBy(KDPoint(offsetX, offsetY)));
101  }
102 
103  /* Handle cases when the size of the view has decreased. */
104  KDCoordinate contentOffsetX = contentOffset().x();
105  KDCoordinate contentOffsetY = contentOffset().y();
106  if (maxContentHeightDisplayableWithoutScrolling() > contentSize().height()-contentOffsetY) {
108  }
109  if (maxContentWidthDisplayableWithoutScrolling() > contentSize().width()-contentOffsetX) {
111  }
112  setContentOffset(KDPoint(contentOffsetX, contentOffsetY));
113 }
114 
115 void ScrollView::scrollToContentRect(KDRect rect, bool allowOverscroll) {
116  scrollToContentPoint(rect.topLeft(), allowOverscroll);
117  scrollToContentPoint(rect.bottomRight(), allowOverscroll);
118 }
119 
121  return KDRect(
122  contentOffset().x(),
123  contentOffset().y(),
124  m_frame.width() - m_leftMargin - m_rightMargin,
125  m_frame.height() - m_topMargin - m_bottomMargin
126  );
127 }
128 
130  // Layout contentView
131  // We're only re-positionning the contentView, not modifying its size.
132  KDPoint absoluteOffset = contentOffset().opposite().translatedBy(KDPoint(m_leftMargin, m_topMargin));
133  KDRect contentFrame = KDRect(absoluteOffset, m_contentView->bounds().size());
134  m_contentView->setFrame(contentFrame);
135 
136  // We recompute the size of the scroll indicator
138 
139  // Layout indicators
140  /* If the two indicators are visible, we leave an empty rectangle in the right
141  * bottom corner. Otherwise, the only indicator uses all the height/width. */
142  if (hasHorizontalIndicator() && hasVerticalIndicator()) {
143  KDRect verticalIndicatorFrame = KDRect(
144  m_frame.width() - m_indicatorThickness, 0,
145  m_indicatorThickness, m_frame.height() - m_indicatorThickness
146  );
147  m_verticalScrollIndicator.setFrame(verticalIndicatorFrame);
148  KDRect horizontalIndicatorFrame = KDRect(
149  0, m_frame.height() - m_indicatorThickness,
150  m_frame.width() - m_indicatorThickness, m_indicatorThickness
151  );
152  m_horizontalScrollIndicator.setFrame(horizontalIndicatorFrame);
153  } else {
154  if (hasVerticalIndicator()) {
155  KDRect verticalIndicatorFrame = KDRect(
156  m_frame.width() - m_indicatorThickness, 0,
157  m_indicatorThickness, m_frame.height()
158  );
159  m_verticalScrollIndicator.setFrame(verticalIndicatorFrame);
160  }
161  if (hasHorizontalIndicator()) {
162  KDRect horizontalIndicatorFrame = KDRect(
163  0, m_frame.height() - m_indicatorThickness,
164  m_frame.width(), m_indicatorThickness
165  );
166  m_horizontalScrollIndicator.setFrame(horizontalIndicatorFrame);
167  }
168  }
169 }
170 
172  if (!m_showsIndicators) {
173  return;
174  }
175  float contentHeight = m_contentView->bounds().height()+m_topMargin+m_bottomMargin;
176  bool hadVerticalIndicator = hasVerticalIndicator();
177  float verticalStart = contentOffset().y();
178  float verticalEnd = contentOffset().y() + m_frame.height();
179  m_verticalScrollIndicator.setStart(verticalStart/contentHeight);
180  m_verticalScrollIndicator.setEnd(verticalEnd/contentHeight);
181  if (hadVerticalIndicator && !hasVerticalIndicator()) {
182  markRectAsDirty(m_verticalScrollIndicator.frame());
183  }
184  float contentWidth = m_contentView->bounds().width()+m_leftMargin+m_rightMargin;
185  bool hadHorizontalIndicator = hasHorizontalIndicator();
186  float horizontalStart = contentOffset().x();
187  float horizontalEnd = contentOffset().x() + m_frame.width();
188  m_horizontalScrollIndicator.setStart(horizontalStart/contentWidth);
189  m_horizontalScrollIndicator.setEnd(horizontalEnd/contentWidth);
190  if (hadHorizontalIndicator && !hasHorizontalIndicator()) {
191  markRectAsDirty(m_horizontalScrollIndicator.frame());
192  }
193 }
194 
197 }
198 
199 void ScrollView::setContentOffset(KDPoint offset, bool forceRelayout) {
200  if (m_dataSource->setOffset(offset) || forceRelayout) {
201  layoutSubviews();
202  }
203 }
204 
206  return m_frame.width() - m_leftMargin - m_rightMargin;
207 }
208 
210  return m_frame.height() - m_topMargin - m_bottomMargin;
211 }
212 
213 #if ESCHER_VIEW_LOGGING
214 const char * ScrollView::className() const {
215  return "ScrollView";
216 }
217 
218 void ScrollView::logAttributes(std::ostream &os) const {
219  View::logAttributes(os);
220  os << " offset=\"" << (int)contentOffset().x << "," << (int)contentOffset().y << "\"";
221 }
222 #endif
KDCoordinate maxContentHeightDisplayableWithoutScrolling()
void setLeftMargin(KDCoordinate m)
Definition: scroll_view.h:19
KDCoordinate y() const
Definition: point.h:11
void scrollToContentRect(KDRect rect, bool allowOverscroll=false)
#define assert(e)
Definition: assert.h:9
bool contains(KDPoint p) const
Definition: rect.cpp:104
KDPoint bottomRight() const
Definition: rect.h:48
void setFrame(KDRect frame)
Definition: view.cpp:125
void setBottomMargin(KDCoordinate m)
Definition: scroll_view.h:17
static constexpr KDCoordinate CommonRightMargin
Definition: metric.h:9
int16_t KDCoordinate
Definition: coordinate.h:6
constexpr KDCoordinate width() const
Definition: size.h:10
bool setOffset(KDPoint offset)
void markRectAsDirty(KDRect rect)
Definition: view.cpp:39
void scrollToContentPoint(KDPoint p, bool allowOverscroll=false)
Definition: scroll_view.cpp:80
KDCoordinate top() const
Definition: rect.h:42
void layoutSubviews() override
KDCoordinate maxContentWidthDisplayableWithoutScrolling()
void updateScrollIndicator()
Definition: point.h:6
KDSize contentSize()
void setCommonMargins()
Definition: scroll_view.cpp:27
Definition: size.h:6
KDCoordinate right() const
Definition: rect.h:43
KDRect m_frame
Definition: view.h:66
void setTopMargin(KDCoordinate m)
Definition: scroll_view.h:13
KDCoordinate left() const
Definition: rect.h:45
static constexpr KDCoordinate CommonBottomMargin
Definition: metric.h:11
void drawRect(KDContext *ctx, KDRect rect) const override
Definition: scroll_view.cpp:64
static constexpr KDCoordinate CommonLeftMargin
Definition: metric.h:8
void setContentOffset(KDPoint offset, bool forceRelayout=false)
virtual KDSize minimalSizeForOptimalDisplay() const
Definition: view.cpp:183
Definition: rect.h:26
void fillRect(KDRect rect, KDColor color)
Definition: context_rect.cpp:8
KDRect visibleContentRect()
KDSize size() const
Definition: rect.h:41
void setRightMargin(KDCoordinate m)
Definition: scroll_view.h:15
KDPoint topLeft() const
Definition: rect.h:47
KDPoint contentOffset() const
Definition: scroll_view.h:41
Definition: view.h:23
void setStart(float start)
KDPoint opposite() const
Definition: point.cpp:7
static constexpr KDCoordinate CommonTopMargin
Definition: metric.h:10
KDCoordinate width() const
Definition: rect.h:39
KDCoordinate x() const
Definition: point.h:10
ScrollView(View *contentView, ScrollViewDataSource *dataSource)
Definition: scroll_view.cpp:9
KDPoint translatedBy(KDPoint other) const
Definition: point.cpp:3
KDCoordinate height() const
Definition: rect.h:40
KDRect bounds() const
Definition: view.cpp:157
#define true
Definition: stdbool.h:8
View * m_contentView
Definition: scroll_view.h:56
KDCoordinate bottom() const
Definition: rect.h:44
constexpr KDCoordinate height() const
Definition: size.h:11
Definition: palette.h:6