Fixed SimpleScrollAdapter to run in O(log_n) when refreshing the screen. Previously was O(n)

This commit is contained in:
casey 2016-05-07 19:45:30 -07:00
parent 2407547325
commit 8f972ad4d8
3 changed files with 78 additions and 53 deletions

View File

@ -49,7 +49,7 @@ void ScrollableWindow::ScrollToBottom() {
int total = (int) adapter->GetLineCount(this->GetWidth());
int height = this->GetContentHeight();
int actual = total - height + 1;
int actual = total - height;
actual = (actual < 0) ? 0 : actual;
adapter->DrawPage(this->GetContents(), actual);
@ -75,7 +75,7 @@ void ScrollableWindow::ScrollDown(int delta) {
int total = adapter->GetLineCount(this->GetWidth());
int height = this->GetContentHeight();
int optimal = total - height + 1;
int optimal = total - height;
int max = max(0, optimal);
int actual = (int) this->scrollPosition + delta;

View File

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "SimpleScrollAdapter.h"
#include <boost/algorithm/string.hpp>
@ -14,18 +15,11 @@ void SimpleScrollAdapter::SetDisplaySize(size_t width, size_t height) {
if (height != this->height || width != this->width) {
this->height = height;
this->width = width;
this->lineCount = -1;
Reindex();
}
}
size_t SimpleScrollAdapter::GetLineCount(size_t width) {
if (this->lineCount == -1) {
Iterator it = this->entries.begin();
for ( ; it != entries.end(); it++) {
this->lineCount += (*it)->GetLineCount(width);
}
}
return this->lineCount;
}
@ -36,69 +30,86 @@ size_t SimpleScrollAdapter::GetEntryCount() {
void SimpleScrollAdapter::DrawPage(WINDOW* window, size_t lineNumber) {
wclear(window);
/* find the entry at the specified line number. this is really inefficient,
and runs in O(n) time. need to figure out a way to speed this up. */
/* binary search to find where we need to start */
int line = 0, curr = 0;
bool found = false;
Iterator it = this->entries.begin();
for ( ; it != this->entries.end(); it++) {
curr = (*it)->GetLineCount(this->width);
if (line + curr > lineNumber) {
found = true;
break;
}
else {
line += curr;
}
}
size_t offset = this->FindEntryIndex(lineNumber);
Iterator it = this->entries.begin() + offset;
/* if found, the iterator will be pointing at the first visible
element. */
if (found) {
Iterator end = this->entries.end();
size_t remaining = this->height;
size_t w = this->width;
size_t c = lineNumber - line;
Iterator end = this->entries.end();
size_t remaining = this->height;
size_t w = this->width;
size_t c = lineNumber - (*it)->GetIndex();
do {
size_t count = (*it)->GetLineCount(w);
do {
size_t count = (*it)->GetLineCount(w);
for (size_t i = c; i < count; i++) {
wprintw(window, "%s\n", (*it)->GetLine(i, w).c_str());
--remaining;
}
for (size_t i = c; i < count; i++) {
wprintw(window, "%s\n", (*it)->GetLine(i, w).c_str());
--remaining;
}
++it;
c = 0;
} while (it != end && remaining != 0);
}
++it;
c = 0;
} while (it != end && remaining != 0);
}
void SimpleScrollAdapter::AddLine(const std::string& str) {
lineCount = -1;
std::vector<std::string> lines;
boost::algorithm::split(lines, str, boost::is_any_of("\n"));
for (size_t i = 0; i < lines.size(); i++) {
boost::shared_ptr<Entry> entry(new Entry(lines[i]));
entry->SetIndex(this->lineCount++);
entries.push_back(entry);
}
}
size_t SimpleScrollAdapter::FindEntryIndex(int lineNumber) {
if (lineCount == -1) {
Reindex();
}
size_t min = 0, max = this->entries.size();
while (true) {
size_t guess = (min + max) / 2;
Entry* entry = this->entries.at(guess).get();
size_t first = entry->GetIndex();
size_t last = first + entry->GetLineCount(this->width);
if (lineNumber >= first && lineNumber <= last) {
return guess;
}
else if (lineNumber > first) { /* guess too low */
min = guess + 1;
}
else if (lineNumber < last) { /* guess too high */
max = guess - 1;
}
}
}
void SimpleScrollAdapter::Reindex() {
int index = 0;
for (Iterator it = this->entries.begin(); it != this->entries.end(); it++) {
(*it)->SetIndex(index);
index += (*it)->GetLineCount(this->width);
}
}
SimpleScrollAdapter::Entry::Entry(const std::string& value) {
this->value = value;
this->charCount = value.size(); // u8len(value);
this->charCount = value.size();
}
size_t SimpleScrollAdapter::Entry::GetLineCount(size_t width) {
int full = (charCount / width);
int remain = (charCount % width == 0 ? 0 : 1);
return full + remain;
return max(1, full + remain);
}
std::string SimpleScrollAdapter::Entry::GetLine(size_t n, size_t width) {
@ -112,3 +123,11 @@ std::string SimpleScrollAdapter::Entry::GetValue() {
return value;
}
size_t SimpleScrollAdapter::Entry::GetIndex() {
return this->index;
}
void SimpleScrollAdapter::Entry::SetIndex(size_t index) {
this->index = index;
}

View File

@ -2,8 +2,6 @@
#include "stdafx.h"
#include <boost/shared_ptr.hpp>
#include <curses.h>
#include "IScrollAdapter.h";
class SimpleScrollAdapter : public IScrollAdapter {
@ -18,23 +16,31 @@ class SimpleScrollAdapter : public IScrollAdapter {
virtual void AddLine(const std::string& str);
private:
size_t lineCount, width, height;
class Entry : public IEntry {
class Entry {
public:
Entry(const std::string& value);
virtual size_t GetLineCount(size_t width);
virtual std::string GetLine(size_t line, size_t width);
virtual std::string GetValue();
size_t GetIndex();
void SetIndex(size_t index);
size_t GetLineCount(size_t width);
std::string GetLine(size_t line, size_t width);
std::string GetValue();
private:
size_t index;
std::string value;
size_t charCount;
};
private:
typedef std::vector<boost::shared_ptr<Entry>> EntryList;
typedef EntryList::iterator Iterator;
void Reindex();
size_t FindEntryIndex(int index);
EntryList entries;
size_t lineCount, width, height;
};