Set each couplet in a dim rounded card, on every screen size

Each line of verse now sits in its own soft card (surface-container-high,
12dp corners), so the eye finds where one couplet ends and the next
begins, and the couplet's chevron, actions and summary visibly belong to
it. On OLED black that step is all but black, so the card takes the next
one up there. Prose stays bare: a paragraph in a box reads as a quotation.

Text keeps at least 6:1 on the card in every theme. The phone layout is
otherwise unchanged.

Updates the Couplet notes, the stylesheet and wireframes 4-8, and adds
wireframe 9 for the phone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RhmDdrN5hgWrgRcFLAMsV6
This commit is contained in:
Claude 2026-10-07 16:04:08 +00:00
parent aab8b2e3e1
commit 64a67ddbbd
No known key found for this signature in database
10 changed files with 33 additions and 4 deletions

View File

@ -7,6 +7,10 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkHorizontally import androidx.compose.animation.shrinkHorizontally
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -251,6 +255,9 @@ fun PoemScreen(
} }
} }
/** The positions that make a line of verse; anything else (Single, Paragraph, Comment) is prose. */
private val VERSE_POSITIONS = setOf(Verse.RIGHT, Verse.LEFT, Verse.CENTERED_1, Verse.CENTERED_2)
/** A word someone tapped: what to look up, the couplet it came from, and where it sits in its verse. */ /** A word someone tapped: what to look up, the couplet it came from, and where it sits in its verse. */
private data class WordTap(val word: String, val passage: Bookmark, val verse: Int, val range: IntRange) private data class WordTap(val word: String, val passage: Bookmark, val verse: Int, val range: IntRange)
@ -379,7 +386,26 @@ private fun Couplet(
val oneLine = sideBySide && couplet.size == 2 && val oneLine = sideBySide && couplet.size == 2 &&
couplet[0].position == Verse.RIGHT && couplet[1].position == Verse.LEFT couplet[0].position == Verse.RIGHT && couplet[1].position == Verse.LEFT
Column(modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)) { // Each line of verse sits in its own soft card, so the eye finds where one couplet ends and
// the next begins, and the couplet's actions visibly belong to it. Prose (Golestan,
// Nowruznameh) stays bare: a paragraph in a box reads as a quotation, not as the text.
val isVerse = couplet.all { it.position in VERSE_POSITIONS }
val colors = MaterialTheme.colorScheme
// A step lighter than the page. On OLED black the usual step is all but black itself, so
// the card takes the next one up: still dim, but there.
val cardColor =
if (colors.surface == Color.Black) colors.surfaceContainerHighest else colors.surfaceContainerHigh
val card = if (isVerse) {
Modifier
.padding(vertical = 4.dp)
.clip(RoundedCornerShape(12.dp))
.background(cardColor)
.padding(horizontal = 12.dp, vertical = 8.dp)
} else {
Modifier.padding(vertical = 6.dp)
}
Column(modifier = Modifier.fillMaxWidth().then(card)) {
if (oneLine) { if (oneLine) {
Row(modifier = Modifier.fillMaxWidth()) { Row(modifier = Modifier.fillMaxWidth()) {
couplet.forEachIndexed { index, verse -> couplet.forEachIndexed { index, verse ->

View File

@ -1,6 +1,6 @@
# ColumnBrowser # ColumnBrowser
The layout for tablets and unfolded foldables. A narrow column of poets sits on the right. Beside it is a column for each level of the open book (books › chapters › poems), and the open page fills the rest of the screen on the left. Phones keep today's navigation. Built in `app/src/main/java/com/ganjoor/android/ui/ColumnBrowser.kt`; wireframes are in `design/wireframes/` (1–8). The layout for tablets and unfolded foldables. A narrow column of poets sits on the right. Beside it is a column for each level of the open book (books › chapters › poems), and the open page fills the rest of the screen on the left. Phones keep today's navigation. Built in `app/src/main/java/com/ganjoor/android/ui/ColumnBrowser.kt`; wireframes are in `design/wireframes/` (1–9).
## Window sizes ## Window sizes

View File

@ -4,7 +4,9 @@ One line of poetry: two hemistichs stacked on a phone, the first pushed to the s
- **Consumer provides:** the couplet's verses (each with its position), the reading style from settings (`poem-naskh` or `poem-nastaliq` at the reader's size and weight), and handlers for a tapped word and for the actions. - **Consumer provides:** the couplet's verses (each with its position), the reading style from settings (`poem-naskh` or `poem-nastaliq` at the reader's size and weight), and handlers for a tapped word and for the actions.
- Alignment by verse position: first hemistich `start`, second `end`, centred verses `center`, prose (single/paragraph/comment) `justify` so Golestan and Nowruznameh fill the column. - Alignment by verse position: first hemistich `start`, second `end`, centred verses `center`, prose (single/paragraph/comment) `justify` so Golestan and Nowruznameh fill the column.
- `space-6` above and below each couplet. Text is `on-surface`. - Every line of verse sits in its own **card**: `surface-container-high` (`surface-container-highest` on OLED black, where the usual step is all but black), `radius-md` (12dp) corners, 12dp/8dp inner padding and 4dp above and below. The card holds the couplet's options chevron, its actions and its summary, so they visibly belong to it. Text is `on-surface`, at least 6:1 on the card in every theme.
- Prose (Single, Paragraph or Comment positions: Golestan, Nowruznameh) has no card; it keeps `space-6` above and below. A paragraph in a box would read as a quotation.
- The cards are the same on phones, tablets and foldables (wireframes 4–9).
- Tapping a word opens the dictionary sheet; tapping between words, or the 32px chevron (`touch-compact`, 20px icon, `on-surface-variant`), opens the couplet's actions: **Save this passage**, **Copy**, **Share** as TextButtons. Save keeps the link back to the poem; Copy doesn't. - Tapping a word opens the dictionary sheet; tapping between words, or the 32px chevron (`touch-compact`, 20px icon, `on-surface-variant`), opens the couplet's actions: **Save this passage**, **Copy**, **Share** as TextButtons. Save keeps the link back to the poem; Copy doesn't.
- Long-press belongs to text selection — never bind actions to it here. - Long-press belongs to text selection — never bind actions to it here.
- With summaries on, Ganjoor's couplet summary sits underneath in `body-small` / `on-surface-variant`. - With summaries on, Ganjoor's couplet summary sits underneath in `body-small` / `on-surface-variant`.

View File

@ -28,7 +28,8 @@
.gj-metre { font-size: 12px; line-height: 16px; letter-spacing: .4px; color: var(--on-surface-variant); } .gj-metre { font-size: 12px; line-height: 16px; letter-spacing: .4px; color: var(--on-surface-variant); }
/* Couplet: two hemistichs stacked; first to the start, second to the end. */ /* Couplet: two hemistichs stacked; first to the start, second to the end. */
.gj-couplet { padding: var(--space-6) 0; } .gj-couplet { margin: var(--space-4) 0; padding: var(--space-8) var(--space-12); border-radius: var(--radius-md); background: var(--surface-container-high); }
.gj-couplet.is-prose { margin: 0; padding: var(--space-6) 0; background: none; }
.gj-verse { font-family: var(--font-naskh); font-size: 22px; line-height: 1.8; color: var(--on-surface); margin: 0; } .gj-verse { font-family: var(--font-naskh); font-size: 22px; line-height: 1.8; color: var(--on-surface); margin: 0; }
.gj-couplet.is-nastaliq .gj-verse { font-family: var(--font-nastaliq); line-height: 2.4; } .gj-couplet.is-nastaliq .gj-verse { font-family: var(--font-nastaliq); line-height: 2.4; }
.gj-verse.is-first { text-align: start; } .gj-verse.is-first { text-align: start; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB