mirror of
https://github.com/MinimalBible/MinimalBible
synced 2024-12-22 14:48:23 -05:00
Add spans for chapter start and verse superscript
Needs serious refactoring, but quickly approaching a minimally viable product. This is awesome.
This commit is contained in:
parent
799d8e2637
commit
320159f1bd
@ -12,6 +12,11 @@ import org.crosswire.jsword.versification.BibleBook
|
||||
import org.bspeice.minimalbible.activity.viewer.BookAdapter.ChapterInfo
|
||||
import rx.subjects.PublishSubject
|
||||
import org.bspeice.minimalbible.service.lookup.VerseLookup
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.StyleSpan
|
||||
import android.graphics.Typeface
|
||||
import android.text.style.SuperscriptSpan
|
||||
import android.text.style.RelativeSizeSpan
|
||||
|
||||
/**
|
||||
* Adapter used for displaying a book
|
||||
@ -25,8 +30,16 @@ class BookAdapter(val b: Book, val lookup: VerseLookup)
|
||||
val bookList = versification.getBooks()
|
||||
val chapterCount = bookList.map { versification.getLastChapter(it) - 1 }.sum()
|
||||
|
||||
/**
|
||||
* Store information needed to decode the text of a chapter
|
||||
* Book, chapter, and bibleBook should be pretty self-explanatory
|
||||
* The vStart, vEnd, and vOffset are needed to map between verses relative to their chapter,
|
||||
* and the actual verse ordinal needed for parsing the text.
|
||||
* So Genesis 1:1 would be chapter 1, bibleBook Genesis, vStart 1, vOffset 2
|
||||
* since it actually starts at ordinal 3
|
||||
*/
|
||||
data class ChapterInfo(val book: Book, val chapter: Int, val bibleBook: BibleBook,
|
||||
val vStart: Int, val vEnd: Int)
|
||||
val vStart: Int, val vEnd: Int, val vOffset: Int)
|
||||
|
||||
/**
|
||||
* A list of all ChapterInfo objects needed for displaying a book
|
||||
@ -37,17 +50,22 @@ class BookAdapter(val b: Book, val lookup: VerseLookup)
|
||||
* } yield ChapterInfo(...)
|
||||
*
|
||||
* Also note that getLastVerse() returns the number of verses in a chapter,
|
||||
* so we build the actual last verse by adding getFirstVerse and getLastVerse
|
||||
* not the actual last verse's ordinal
|
||||
*/
|
||||
// TODO: Lazy compute values needed for this list
|
||||
val chapterList: List<ChapterInfo> = bookList.flatMap {
|
||||
val currentBook = it
|
||||
(1..versification.getLastChapter(currentBook)).map {
|
||||
val firstVerse = versification.getFirstVerse(currentBook, it)
|
||||
val firstVerseOrdinal = versification.getFirstVerse(currentBook, it)
|
||||
val verseOrdinalOffset = firstVerseOrdinal - 1
|
||||
val verseCount = versification.getLastVerse(currentBook, it)
|
||||
ChapterInfo(b, it, currentBook, firstVerse, firstVerse + verseCount)
|
||||
val firstVerseRelative = 1
|
||||
val lastVerseRelative = firstVerseRelative + verseCount
|
||||
|
||||
ChapterInfo(b, it, currentBook,
|
||||
firstVerseRelative, lastVerseRelative, verseOrdinalOffset)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* I'm not sure what the position argument actually represents,
|
||||
@ -94,13 +112,82 @@ class BookAdapter(val b: Book, val lookup: VerseLookup)
|
||||
class PassageView(val v: TextView, val b: Book, val lookup: VerseLookup)
|
||||
: RecyclerView.ViewHolder(v) {
|
||||
|
||||
fun getVerseText(verseRange: Progression<Int>) =
|
||||
verseRange.map { lookup.getText(b.getVersification().decodeOrdinal(it)) }
|
||||
// Span to be applied to an individual verse - doesn't know about the sizes
|
||||
// of other verses so that's why start and end are relative
|
||||
/**
|
||||
* A holder object that knows how apply itself to a SpannableStringBuilder
|
||||
* Since we don't know ahead of time where this verse will end up relative to the
|
||||
* entire TextView (since there is one chapter per TextView) we use a start and end
|
||||
* relative to the verse text itself. That is, rStart of 0 indicates verse text start,
|
||||
* and rEnd of (text.length - 1) indicates the end of verse text
|
||||
* @param span The span object we should apply
|
||||
* @param rStart When the span should begin, relative to the verse
|
||||
* @param rEnd When the span should end, relative to the verse
|
||||
*/
|
||||
data class SpanHolder(val span: Any?, val rStart: Int, val rEnd: Int) {
|
||||
// TODO: Is there a more case-class like way of doing this?
|
||||
class object {
|
||||
val EMPTY = SpanHolder(null, 0, 0)
|
||||
}
|
||||
/**
|
||||
* Apply this span object to the specified builder
|
||||
* Tries to be as close to immutable as possible. The offset is used to calculate
|
||||
* the absolute position of when this span should start and end, since
|
||||
* rStart and rEnd are relative to the verse text, and know nothing about the
|
||||
* rest of the text in the TextView
|
||||
*/
|
||||
fun apply(builder: SpannableStringBuilder, offset: Int): SpannableStringBuilder {
|
||||
if (span != null)
|
||||
builder.setSpan(span, rStart + offset, rEnd + offset, 0)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
|
||||
fun reduceText(verses: List<String>) = verses.join(" ")
|
||||
// TODO: getRawVerse shouldn't know how to decode ints
|
||||
fun getRawVerse(verse: Int): String =
|
||||
lookup.getText(b.getVersification().decodeOrdinal(verse))
|
||||
|
||||
// TODO: This code is nasty, not sure how to refactor, but it needs doing
|
||||
fun getProcessedVerse(verseOrdinal: Int, info: ChapterInfo): Pair<String, List<SpanHolder>> {
|
||||
val rawText = getRawVerse(verseOrdinal)
|
||||
// To be honest, I have no idea why I need to subtract one. But I do.
|
||||
val relativeVerse = verseOrdinal - info.vOffset - 1
|
||||
val processedText = when (relativeVerse) {
|
||||
0 -> ""
|
||||
1 -> "${info.chapter} $rawText"
|
||||
else -> "$relativeVerse$rawText"
|
||||
}
|
||||
val spans: List<SpanHolder> = listOf(
|
||||
when (relativeVerse) {
|
||||
0 -> SpanHolder.EMPTY
|
||||
1 -> SpanHolder(StyleSpan(Typeface.BOLD), 0, info.chapter.toString().length)
|
||||
else -> SpanHolder(SuperscriptSpan(), 0, relativeVerse.toString().length)
|
||||
}
|
||||
)
|
||||
val secondSpan =
|
||||
if (relativeVerse > 1)
|
||||
// TODO: No magic numbers!
|
||||
spans plus SpanHolder(RelativeSizeSpan(0.6f), 0, relativeVerse.toString().length)
|
||||
else
|
||||
spans
|
||||
|
||||
return Pair(processedText, secondSpan)
|
||||
}
|
||||
|
||||
fun getAllVerses(verses: Progression<Int>, info: ChapterInfo) =
|
||||
verses.map { getProcessedVerse(it + info.vOffset, info) }
|
||||
// For each verse, get the text
|
||||
.fold(SpannableStringBuilder(), { initialBuilder, versePair ->
|
||||
val offset = initialBuilder.length()
|
||||
val builderWithText = initialBuilder append versePair.first
|
||||
|
||||
// And apply all spans
|
||||
versePair.second.fold(builderWithText, { postBuilder, span ->
|
||||
span.apply(postBuilder, offset)
|
||||
})
|
||||
})
|
||||
|
||||
// Uses functional style, but those parentheses man... you'd think I was writing LISP
|
||||
fun bind(info: ChapterInfo) {
|
||||
v.setText(reduceText(getVerseText(info.vStart..info.vEnd)))
|
||||
v setText getAllVerses(info.vStart..info.vEnd, info)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user