mirror of
https://github.com/MinimalBible/MinimalBible
synced 2024-11-05 07:38:20 -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 org.bspeice.minimalbible.activity.viewer.BookAdapter.ChapterInfo
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import org.bspeice.minimalbible.service.lookup.VerseLookup
|
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
|
* Adapter used for displaying a book
|
||||||
@ -25,8 +30,16 @@ class BookAdapter(val b: Book, val lookup: VerseLookup)
|
|||||||
val bookList = versification.getBooks()
|
val bookList = versification.getBooks()
|
||||||
val chapterCount = bookList.map { versification.getLastChapter(it) - 1 }.sum()
|
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,
|
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
|
* 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(...)
|
* } yield ChapterInfo(...)
|
||||||
*
|
*
|
||||||
* Also note that getLastVerse() returns the number of verses in a chapter,
|
* 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
|
// TODO: Lazy compute values needed for this list
|
||||||
val chapterList: List<ChapterInfo> = bookList.flatMap {
|
val chapterList: List<ChapterInfo> = bookList.flatMap {
|
||||||
val currentBook = it
|
val currentBook = it
|
||||||
(1..versification.getLastChapter(currentBook)).map {
|
(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)
|
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,
|
* 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)
|
class PassageView(val v: TextView, val b: Book, val lookup: VerseLookup)
|
||||||
: RecyclerView.ViewHolder(v) {
|
: RecyclerView.ViewHolder(v) {
|
||||||
|
|
||||||
fun getVerseText(verseRange: Progression<Int>) =
|
// Span to be applied to an individual verse - doesn't know about the sizes
|
||||||
verseRange.map { lookup.getText(b.getVersification().decodeOrdinal(it)) }
|
// 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) {
|
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