Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3486a89b56 | ||
|
|
701b984f4b | ||
|
|
41d776a35d | ||
|
|
bcdad2e699 | ||
|
|
ce26676d4e | ||
|
|
f60f9b4079 | ||
|
|
936fd3cf3b |
No files matched your search
@@ -97,6 +97,22 @@ fun PoemScreen(
|
||||
// finds, so the couplet's own actions live at its foot rather than behind a tap between words.
|
||||
var tapped by remember { mutableStateOf<WordTap?>(null) }
|
||||
|
||||
// One side panel at a time. The dictionary and the reading settings both want the left of the
|
||||
// screen, and opening the second put two panels there at once — or, where there was no longer
|
||||
// room for two, left the dictionary as a sheet in the middle of the page while the settings
|
||||
// sat beside it. Either way the reader is asked to look in two places. The settings replace
|
||||
// the dictionary instead; closing them leaves the poem, which is where the reader was.
|
||||
val sidePanelOpen = LocalSidePanelOpen.current
|
||||
LaunchedEffect(sidePanelOpen) { if (sidePanelOpen) tapped = null }
|
||||
|
||||
BoxWithConstraints(Modifier.fillMaxSize()) {
|
||||
// Whether the dictionary gets a column of its own is not a question about the window but
|
||||
// about what is left of this page once the columns have taken theirs. On a book-style
|
||||
// foldable held open in portrait the page is already down to ~450dp, and a panel beside it
|
||||
// left the verse a couple of characters a line. Where it does not fit, the sheet is the
|
||||
// better answer: it covers the foot of the poem but leaves the lines whole.
|
||||
val roomForPanel = dictionaryFitsBeside(maxWidth, wide)
|
||||
|
||||
Load(
|
||||
key = fullUrl,
|
||||
block = { Ganjoor.poem(fullUrl) },
|
||||
@@ -231,7 +247,7 @@ fun PoemScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wide) {
|
||||
if (roomForPanel) {
|
||||
AnimatedVisibility(
|
||||
visible = tapped != null,
|
||||
enter = expandHorizontally() + fadeIn(),
|
||||
@@ -249,7 +265,7 @@ fun PoemScreen(
|
||||
}
|
||||
}
|
||||
|
||||
if (!wide) {
|
||||
if (!roomForPanel) {
|
||||
tapped?.let { tap ->
|
||||
WordSheet(
|
||||
word = tap.word,
|
||||
@@ -258,6 +274,7 @@ fun PoemScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The positions that make a line of verse; anything else (Single, Paragraph, Comment) is prose. */
|
||||
|
||||
@@ -39,6 +39,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ganjoor.android.R
|
||||
import com.ganjoor.android.data.Definition
|
||||
@@ -48,6 +49,37 @@ import com.ganjoor.android.data.LocalAssistant
|
||||
import com.ganjoor.android.data.Pronunciation
|
||||
import com.ganjoor.android.ui.theme.readingStyle
|
||||
|
||||
/**
|
||||
* How wide the dictionary sits beside the poem.
|
||||
*
|
||||
* Narrower than the reading settings, deliberately: the settings fold the columns away and take
|
||||
* the room that frees, while the dictionary is read *against* the line it came from, so the verse
|
||||
* keeps the width instead. A definition is short; a hemistich is not.
|
||||
*/
|
||||
val DictionaryPanelWidth = 216.dp
|
||||
|
||||
/**
|
||||
* The narrowest the page may be left once the dictionary opens beside it. Below this the panel is
|
||||
* not worth its room: on a book-style foldable held open in portrait the columns have already
|
||||
* taken theirs, and a panel there left the verse a couple of characters a line.
|
||||
*/
|
||||
val MinPageMeasure = 400.dp
|
||||
|
||||
/**
|
||||
* Whether the dictionary earns a column of its own beside a page [pageWidth] wide.
|
||||
*
|
||||
* Not a question about the window but about the page: by the time a poem is open the columns have
|
||||
* already taken their room, and on a book-style foldable held open in portrait what is left is
|
||||
* about 450dp. A panel beside that leaves the verse a couple of characters a line, so below
|
||||
* [MinPageMeasure] the bottom sheet is used instead — it covers the foot of the poem but leaves
|
||||
* every line whole.
|
||||
*
|
||||
* A pure function so the rule can be tested: the gesture that reaches it cannot be driven
|
||||
* reliably, but the arithmetic behind it can.
|
||||
*/
|
||||
internal fun dictionaryFitsBeside(pageWidth: Dp, wide: Boolean): Boolean =
|
||||
wide && pageWidth - DictionaryPanelWidth >= MinPageMeasure
|
||||
|
||||
/** English prose inside an otherwise right-to-left sheet. */
|
||||
@Composable
|
||||
private fun LeftToRight(content: @Composable () -> Unit) {
|
||||
@@ -102,7 +134,7 @@ fun WordPanel(word: String, onDismiss: () -> Unit, passage: Bookmark? = null) {
|
||||
BackHandler(onBack = onDismiss)
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||
modifier = Modifier.width(360.dp).fillMaxHeight(),
|
||||
modifier = Modifier.width(DictionaryPanelWidth).fillMaxHeight(),
|
||||
) {
|
||||
Column(Modifier.statusBarsPadding()) {
|
||||
Row(
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.ganjoor.android
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ganjoor.android.ui.dictionaryFitsBeside
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* The rule that decides whether the dictionary opens beside the poem or as a sheet over it.
|
||||
*
|
||||
* Driving the gesture that reaches it — a tap landing on the glyphs of one Persian word — is not
|
||||
* something adb does reliably, so the arithmetic is tested directly instead.
|
||||
*/
|
||||
class DictionaryPanelFitTest {
|
||||
|
||||
@Test
|
||||
fun `a tablet has room beside the poem`() {
|
||||
// 1280dp window, less the poets rail and two list columns, leaves the page about 900dp.
|
||||
assertTrue(dictionaryFitsBeside(900.dp, wide = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a foldable open in portrait does not`() {
|
||||
// 700dp window, less the rail and the newest column, leaves about 450dp — and 450 less
|
||||
// the 216dp panel is 234dp, which is a cramped phone column, not a measure for a verse.
|
||||
assertFalse(dictionaryFitsBeside(450.dp, wide = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a phone never gets the panel, however the page is measured`() {
|
||||
assertFalse(dictionaryFitsBeside(420.dp, wide = false))
|
||||
assertFalse(dictionaryFitsBeside(2000.dp, wide = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `the boundary is the panel plus the minimum measure`() {
|
||||
assertTrue(dictionaryFitsBeside(616.dp, wide = true)) // 400 + 216, exactly
|
||||
assertFalse(dictionaryFitsBeside(615.dp, wide = true))
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,10 @@ On large screens, tapping a word opens **WordPanel** on the left instead of the
|
||||
- The poem moves over to make room, so nothing being read is covered.
|
||||
- The tapped word stays highlighted in the verse (`secondary-container` / `on-secondary-container`), on phones too.
|
||||
- ✕ or Back closes the panel.
|
||||
- The panel only opens where the page keeps at least **400dp** beside it. On a book-style
|
||||
foldable held open in portrait the columns have already taken their room, and a panel
|
||||
there left the verse a couple of characters a line. Below that the sheet is used instead:
|
||||
it covers the foot of the poem but leaves the lines whole.
|
||||
|
||||
## Reading settings
|
||||
|
||||
|
||||
Reference in new issue
Block a user