Escher: Use dirty-tracking for View::redraw

Change-Id: I95da4eee9218784744ac4abc53328d3f537bede6
This commit is contained in:
Romain Goyet
2016-06-01 17:02:57 +02:00
parent 4a1497e659
commit 93ca2f6475
15 changed files with 67 additions and 38 deletions

View File

@@ -7,7 +7,7 @@ class ChildlessView : public View {
using View::View;
protected:
int numberOfSubviews() const override;
const View * subview(int index) const override;
View * subview(int index) override;
void storeSubviewAtIndex(View * v, int index) override;
void layoutSubviews() override;
};

View File

@@ -8,7 +8,7 @@ public:
ScrollView(View * contentView);
int numberOfSubviews() const override;
const View * subview(int index) const override;
View * subview(int index) override;
void storeSubviewAtIndex(View * view, int index) override;
void layoutSubviews() override;

View File

@@ -10,7 +10,7 @@ class TabView : public View {
public:
TabView();
int numberOfSubviews() const override;
const View * subview(int index) const override;
View * subview(int index) override;
void layoutSubviews() override;
void addTabNamed(const char * name);

View File

@@ -22,7 +22,7 @@ private:
ContentView();
int numberOfSubviews() const override;
const View * subview(int index) const override;
View * subview(int index) override;
void storeSubviewAtIndex(View * view, int index) override;
void layoutSubviews() override;

View File

@@ -22,6 +22,7 @@ extern "C" {
class Window;
class View {
friend class Window;
public:
View();
@@ -30,12 +31,17 @@ public:
//void addSubview(View * subview);
//void removeFromSuperview();
void setFrame(KDRect frame);
void markAsNeedingRedraw();
/*
void markAsDirty() const;
void redraw() const;
*/
void setSubview(View * v, int index);
KDRect bounds() const;
#if ESCHER_VIEW_LOGGING
friend std::ostream &operator<<(std::ostream &os, const View &view);
friend std::ostream &operator<<(std::ostream &os, View &view);
#endif
protected:
#if ESCHER_VIEW_LOGGING
@@ -44,15 +50,16 @@ protected:
#endif
virtual const Window * window() const;
virtual int numberOfSubviews() const = 0;
virtual const View * subview(int index) const = 0;
virtual View * subview(int index) = 0;
virtual void storeSubviewAtIndex(View * v, int index) = 0;
virtual void layoutSubviews() = 0;
private:
void redraw(KDRect rect) const;
void redraw(KDRect rect);
KDRect absoluteDrawingArea() const;
View * m_superview;
KDRect m_frame;
bool m_needsRedraw;
//TODO: We may want a dynamic size at some point
/*
static constexpr uint8_t k_maxNumberOfSubviews = 4;

View File

@@ -6,13 +6,14 @@
class Window : public View {
public:
Window();
void redraw();
protected:
#if ESCHER_VIEW_LOGGING
const char * className() const override;
#endif
const Window * window() const override;
int numberOfSubviews() const override;
const View * subview(int index) const override;
View * subview(int index) override;
void layoutSubviews() override;
void storeSubviewAtIndex(View * view, int index) override;
private:

View File

@@ -17,6 +17,7 @@ void App::run() {
while (true) {
ion_event_t event = ion_get_event(); // This is a blocking call
dispatchEvent(event);
window.redraw();
}
}

View File

@@ -7,7 +7,7 @@ int ChildlessView::numberOfSubviews() const {
return 0;
}
const View * ChildlessView::subview(int index) const {
View * ChildlessView::subview(int index) {
assert(false);
return nullptr;
}

View File

@@ -16,7 +16,7 @@ int ScrollView::numberOfSubviews() const {
return 1;
}
const View * ScrollView::subview(int index) const {
View * ScrollView::subview(int index) {
assert(index == 0);
return m_contentView;
}

View File

@@ -34,12 +34,12 @@ void ScrollViewIndicator::drawRect(KDRect rect) const {
void ScrollViewIndicator::setStart(float start) {
m_start = start;
redraw();
markAsNeedingRedraw();
}
void ScrollViewIndicator::setEnd(float end) {
m_end = end;
redraw();
markAsNeedingRedraw();
}
#if ESCHER_VIEW_LOGGING

View File

@@ -17,7 +17,7 @@ void TabView::addTabNamed(const char * name) {
m_cells[tabIndex].setName(name);
m_numberOfTabs++;
setSubview(&m_cells[tabIndex], tabIndex);
redraw();
markAsNeedingRedraw();
}
void TabView::setActiveIndex(int index) {
@@ -34,7 +34,7 @@ int TabView::numberOfSubviews() const {
return m_numberOfTabs;
}
const View * TabView::subview(int index) const {
View * TabView::subview(int index) {
assert(index < m_numberOfTabs);
return &m_cells[index];
}

View File

@@ -12,12 +12,12 @@ TabViewCell::TabViewCell() :
void TabViewCell::setName(const char * name) {
m_name = name;
redraw();
markAsNeedingRedraw();
}
void TabViewCell::setActive(bool active) {
m_active = active;
redraw();
markAsNeedingRedraw();
}
void TabViewCell::drawRect(KDRect rect) const {

View File

@@ -37,7 +37,7 @@ int TabViewController::ContentView::numberOfSubviews() const {
return 2;
}
const View * TabViewController::ContentView::subview(int index) const {
View * TabViewController::ContentView::subview(int index) {
if (index == 0) {
return &m_tabView;
} else {
@@ -111,7 +111,7 @@ void TabViewController::setActiveTab(uint8_t i) {
m_view.setActiveView(activeVC->view());
m_view.m_tabView.setActiveIndex(i);
m_activeChildIndex = i;
m_view.redraw();
m_view.markAsNeedingRedraw();
}
View * TabViewController::view() {

View File

@@ -5,13 +5,9 @@ extern "C" {
View::View() :
m_superview(nullptr),
m_frame(KDRectZero)
m_frame(KDRectZero),
m_needsRedraw(true)
{
/*
for (uint8_t i=0; i<k_maxNumberOfSubviews; i++) {
m_subviews[i] = nullptr;
}
*/
}
void View::drawRect(KDRect rect) const {
@@ -27,21 +23,36 @@ const Window * View::window() const {
}
}
void View::redraw() const {
redraw(bounds());
void View::markAsNeedingRedraw() {
// Let's mark ourself as needing redraw
m_needsRedraw = true;
/* And let's mark our parents as needing redraw too. The alternative would be
* to have a recursive getter, which would be more resilient to superview
* modification, but much slower too. As long as we don't change the view
* hierarchy, this way is easier. */
if (m_superview) {
m_superview->markAsNeedingRedraw();
}
}
void View::redraw(KDRect rect) const {
if (window() == nullptr) {
void View::redraw(KDRect rect) {
/* CAUTION: do NOT call redraw directly.
* This may seem to work, but will not. Namely, it won't clip.
* Example : our superview is smaller than we are. If we redraw ourself, we
* will overflow our superview. */
if (window() == nullptr || !m_needsRedraw) {
return;
}
// Fisrt, let's draw our own content by calling drawRect
// First, let's draw our own content by calling drawRect
KDSetDrawingArea(absoluteDrawingArea());
this->drawRect(rect);
// Then, let's recursively draw our children over ourself
for (uint8_t i=0; i<numberOfSubviews(); i++) {
const View * subview = this->subview(i);
View * subview = this->subview(i);
if (subview == nullptr) {
continue;
}
@@ -53,13 +64,16 @@ void View::redraw(KDRect rect) const {
subview->redraw(intersection);
}
}
// Eventually, mark that we don't need to be redrawn
m_needsRedraw = false;
}
void View::setSubview(View * view, int index) {
view->m_superview = this;
storeSubviewAtIndex(view, index);
assert(subview(index) == view);
redraw();
view->markAsNeedingRedraw();
}
/*
@@ -98,16 +112,18 @@ void View::removeFromSuperview() {
void View::setFrame(KDRect frame) {
// TODO: Return if frame is equal to m_frame
KDRect previousFrame = m_frame;
m_frame = frame;
if (m_superview != nullptr) {
/* We have moved this view. This left a blank spot in its superview were it
* previously was. So let's redraw that part of the superview. */
m_superview->redraw(previousFrame);
* previously was.
* At this point, we know that the only area that really needs to be redrawn
* in the superview is the value of m_frame at the start of that method.
* However, let's not try to optimize too early, and let's simply mark the
* whole superview as needing redraw. */
m_superview->markAsNeedingRedraw();
}
layoutSubviews();
// The view now needs to redraw itself entirely
redraw();
markAsNeedingRedraw();
}
KDRect View::bounds() const {
@@ -141,7 +157,7 @@ void View::logAttributes(std::ostream &os) const {
os << " frame=\"" << m_frame.x << "," << m_frame.y << "," << m_frame.width << "," << m_frame.height << "\"";
}
std::ostream &operator<<(std::ostream &os, const View &view) {
std::ostream &operator<<(std::ostream &os, View &view) {
os << "<" << view.className();
view.logAttributes(os);
os << ">";

View File

@@ -8,6 +8,10 @@ Window::Window() :
{
}
void Window::redraw() {
View::redraw(bounds());
}
const Window * Window::window() const {
return this;
}
@@ -16,7 +20,7 @@ int Window::numberOfSubviews() const {
return (m_contentView == nullptr ? 0 : 1);
}
const View * Window::subview(int index) const {
View * Window::subview(int index) {
assert(m_contentView != nullptr && index == 0);
return m_contentView;
}