[escher] create a class table view

Change-Id: Ieb339468cdd4cdb5b36c023ed64dcf10c0434b97
This commit is contained in:
Émilie Feral
2016-09-27 18:05:59 +02:00
parent cc03f8116b
commit 97541b170c
4 changed files with 252 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ objs += $(addprefix escher/src/,\
tab_view.o\
tab_view_cell.o\
tab_view_controller.o\
table_view.o\
table_view_cell.o\
text_field.o\
text_view.o\

View File

@@ -19,6 +19,7 @@
#include <escher/text_field.h>
#include <escher/text_view.h>
#include <escher/tab_view_controller.h>
#include <escher/table_view.h>
#include <escher/table_view_cell.h>
#include <escher/tiled_view.h>
#include <escher/view.h>

View File

@@ -0,0 +1,65 @@
#ifndef ESCHER_TABLE_VIEW_H
#define ESCHER_TABLE_VIEW_H
#include <escher/scroll_view.h>
class TableViewDataSource {
public:
virtual int numberOfRows() = 0;
virtual int numberOfColumns() = 0;
virtual void willDisplayCellAtLocation(View * cell, int x, int y);
virtual KDCoordinate cellHeight() = 0;
virtual KDCoordinate cellWidth() = 0;
virtual View * reusableCell(int index) = 0;
virtual int reusableCellCount() = 0;
};
class TableView : public ScrollView {
public:
TableView(TableViewDataSource * dataSource, KDCoordinate topMargin = 0, KDCoordinate rightMargin = 0,
KDCoordinate bottomMargin = 0, KDCoordinate leftMargin = 0);
void scrollToCell(int x, int y);
View * cellAtIndex(int x, int y);
protected:
#if ESCHER_VIEW_LOGGING
const char * className() const override;
#endif
private:
class ContentView : public View {
public:
ContentView(TableView * tableView, TableViewDataSource * dataSource);
KDCoordinate height() const;
KDCoordinate width() const;
void scrollToCell(int x, int y) const;
View * cellAtIndex(int x, int y);
protected:
#if ESCHER_VIEW_LOGGING
const char * className() const override;
#endif
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
int numberOfFullyDisplayableRows() const;
int numberOfFullyDisplayableColumns() const;
int numberOfDisplayableRows() const;
int numberOfDisplayableColumns() const;
int rowsScrollingOffset() const;
int columnsScrollingOffset() const;
bool rowAtIndexIsBeforeFullyVisibleRange(int index) const;
bool columnAtIndexIsBeforeFullyVisibleRange(int index) const;
bool rowAtIndexIsAfterFullyVisibleRange(int index) const;
bool columnAtIndexIsAfterFullyVisibleRange(int index) const;
TableView * m_tableView;
TableViewDataSource * m_dataSource;
};
void layoutSubviews() override;
ContentView m_contentView;
};
#endif

185
escher/src/table_view.cpp Normal file
View File

@@ -0,0 +1,185 @@
#include <escher/table_view.h>
#include <escher/metric.h>
extern "C" {
#include <assert.h>
}
#define MIN(x,y) ((x)<(y) ? (x) : (y))
void TableViewDataSource::willDisplayCellAtLocation(View * cell, int x, int y) {
}
TableView::TableView(TableViewDataSource * dataSource, KDCoordinate topMargin, KDCoordinate rightMargin,
KDCoordinate bottomMargin, KDCoordinate leftMargin) :
ScrollView(&m_contentView, topMargin, rightMargin, bottomMargin, leftMargin),
m_contentView(TableView::ContentView(this, dataSource))
{
}
// This method computes the minimal scrolling needed to properly display the
// requested cell.
void TableView::scrollToCell(int x, int y) {
m_contentView.scrollToCell(x, y);
}
View * TableView::cellAtIndex(int x, int y) {
return m_contentView.cellAtIndex(x, y);
}
#if ESCHER_VIEW_LOGGING
const char * TableView::className() const {
return "TableView";
}
#endif
void TableView::layoutSubviews() {
// We only have to layout our contentView.
// We will size it here, and ScrollView::layoutSubviews will position it.
KDRect contentViewFrame(0, 0, m_contentView.width(), m_contentView.height());
m_contentView.setFrame(contentViewFrame);
ScrollView::layoutSubviews();
}
/* TableView::ContentView */
TableView::ContentView::ContentView(TableView * tableView, TableViewDataSource * dataSource) :
View(),
m_tableView(tableView),
m_dataSource(dataSource)
{
}
KDCoordinate TableView::ContentView::height() const {
return m_dataSource->numberOfRows() * m_dataSource->cellHeight();
}
KDCoordinate TableView::ContentView::width() const {
return m_dataSource->numberOfColumns() * m_dataSource->cellWidth();
}
void TableView::ContentView::scrollToCell(int x, int y) const {
KDCoordinate contentOffsetX = m_tableView->contentOffset().x();
KDCoordinate contentOffsetY = m_tableView->contentOffset().y();
if (columnAtIndexIsBeforeFullyVisibleRange(x)) {
// Let's scroll the tableView to put that cell on the left (while keeping the left margin)
contentOffsetX = x*m_dataSource->cellWidth();
} else if (columnAtIndexIsAfterFullyVisibleRange(x)) {
// Let's scroll the tableView to put that cell on the right (while keeping the right margin)
contentOffsetX = (x+1)*m_dataSource->cellWidth() - m_tableView->maxContentWidthDisplayableWithoutScrolling();
}
if (rowAtIndexIsBeforeFullyVisibleRange(y)) {
// Let's scroll the tableView to put that cell on the top (while keeping the top margin)
contentOffsetY = y*m_dataSource->cellHeight();
} else if (rowAtIndexIsAfterFullyVisibleRange(y)) {
// Let's scroll the tableView to put that cell on the bottom (while keeping the bottom margin)
contentOffsetY = (y+1)*m_dataSource->cellHeight() - m_tableView->maxContentHeightDisplayableWithoutScrolling();
}
m_tableView->setContentOffset(KDPoint(contentOffsetX, contentOffsetY));
}
View * TableView::ContentView::cellAtIndex(int x, int y) {
int relativeX = x-columnsScrollingOffset();
int relativeY = y-rowsScrollingOffset();
return m_dataSource->reusableCell(relativeY*numberOfDisplayableColumns()+relativeX);
}
#if ESCHER_VIEW_LOGGING
const char * TableView::ContentView::className() const {
return "TableView::ContentView";
}
#endif
int TableView::ContentView::numberOfSubviews() const {
int result = numberOfDisplayableRows() * numberOfDisplayableColumns();
assert(result <= m_dataSource->reusableCellCount());
return result;
}
View * TableView::ContentView::subviewAtIndex(int index) {
assert(index >= 0);
assert(index < m_dataSource->reusableCellCount());
return m_dataSource->reusableCell(index);
}
void TableView::ContentView::layoutSubviews() {
int rowOffset = rowsScrollingOffset();
int columnOffset = columnsScrollingOffset();
for (int i=0; i<numberOfSubviews(); i++) {
View * cell = subview(i);
int columns = numberOfDisplayableColumns();
int y = i / columns;
/* "x = i % columns" but we avoid a call to modulo not to implement
* "__aeabi_idivmod" */
int x = i - y * columns;
KDCoordinate cellHeight = m_dataSource->cellHeight();
KDCoordinate cellWidth = m_dataSource->cellWidth();
KDRect cellFrame((columnOffset+x)*cellWidth, (rowOffset+y)*cellHeight,
cellWidth, cellHeight);
cell->setFrame(cellFrame);
m_dataSource->willDisplayCellAtLocation(cell, columnOffset+x, rowOffset+y);
}
}
int TableView::ContentView::numberOfFullyDisplayableRows() const {
// The number of displayable rows taking into accounts margins
return m_tableView->maxContentHeightDisplayableWithoutScrolling()/m_dataSource->cellHeight();
}
int TableView::ContentView::numberOfFullyDisplayableColumns() const {
// The number of displayable columns taking into accounts margins
return m_tableView->maxContentWidthDisplayableWithoutScrolling()/m_dataSource->cellHeight();
}
int TableView::ContentView::numberOfDisplayableRows() const {
return MIN(
m_dataSource->numberOfRows(),
m_tableView->bounds().height()/m_dataSource->cellHeight() + 2
);
}
int TableView::ContentView::numberOfDisplayableColumns() const {
return MIN(
m_dataSource->numberOfColumns(),
m_tableView->bounds().width()/m_dataSource->cellWidth() + 2
);
}
int TableView::ContentView::rowsScrollingOffset() const {
/* Here, we want to translate the offset at which our tableView is displaying
* us into an integer offset we can use to ask cells to our data source. */
KDCoordinate pixelScrollingOffset = -m_frame.y();
return pixelScrollingOffset / m_dataSource->cellHeight();
}
int TableView::ContentView::columnsScrollingOffset() const {
/* Here, we want to translate the offset at which our tableView is displaying
* us into an integer offset we can use to ask cells to our data source. */
KDCoordinate pixelScrollingOffset = -m_frame.x();
return pixelScrollingOffset / m_dataSource->cellWidth();
}
bool TableView::ContentView::rowAtIndexIsBeforeFullyVisibleRange(int index) const {
return index <= rowsScrollingOffset();
}
bool TableView::ContentView::columnAtIndexIsBeforeFullyVisibleRange(int index) const {
return index <= columnsScrollingOffset();
}
bool TableView::ContentView::rowAtIndexIsAfterFullyVisibleRange(int index) const {
int relativeIndex = index - rowsScrollingOffset();
return (relativeIndex >= numberOfFullyDisplayableRows());
}
bool TableView::ContentView::columnAtIndexIsAfterFullyVisibleRange(int index) const {
int relativeIndex = index - columnsScrollingOffset();
return (relativeIndex >= numberOfFullyDisplayableColumns());
}