Compare commits

..
Author SHA1 Message Date
anasandClaude Opus 5 3486a89b56 Test the rule that chooses the dictionary's container
The guard added in #3 decides whether the dictionary opens beside the poem or as
a sheet over it, and until now nothing proved it. I tried four times to verify it
by hand and failed every time: reaching it needs a tap landing on the glyphs of
one Persian word inside a SelectionContainer, and adb's synthetic taps either
became a long press and opened the selection menu, or landed between words and
opened the couplet's actions instead. The verses are not exposed to uiautomator
either, so there is nothing to aim at.

The arithmetic behind the rule can be tested even though the gesture cannot, so
it moves into a pure function and gets four cases: a tablet keeps the panel, a
foldable open in portrait does not, a phone never gets it however wide the page
is measured, and the boundary sits exactly at the panel plus the minimum measure
(616dp passes, 615dp does not).

No behaviour changes — this is the same expression, named and covered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-07 18:43:01 +02:00
7 changed files with 61 additions and 57 deletions

No files matched your search

+3 -3
View File
@@ -16,7 +16,7 @@ val keystoreProperties = Properties().apply {
// A delivered file should say what it is without being opened.
base {
archivesName = "ganjoor-0.4.0"
archivesName = "ganjoor-0.3.0"
}
android {
@@ -32,8 +32,8 @@ android {
// Pre-1.0 while the app is still being shaped. versionCode only ever climbs: F-Droid
// refuses an update that does not, and one changelog file per code lives in
// fastlane/metadata/android/*/changelogs/.
versionCode = 6
versionName = "0.4.0"
versionCode = 5
versionName = "0.3.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -111,7 +111,7 @@ fun PoemScreen(
// 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 = wide && maxWidth - DictionaryPanelWidth >= MinPageMeasure
val roomForPanel = dictionaryFitsBeside(maxWidth, wide)
Load(
key = fullUrl,
@@ -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
@@ -64,6 +65,21 @@ val DictionaryPanelWidth = 216.dp
*/
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) {
@@ -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))
}
}
@@ -1,19 +0,0 @@
Tablets and unfolded foldables now read in columns: the poets on the right, then
a column for each level of the open book, and the poem in whatever room is left.
Each column scrolls on its own and narrows as you read, so the poem keeps the
middle. A button in the top bar folds them away.
Where there is room, a couplet's two hemistichs sit on one line, as Ganjoor sets
them on a desktop. Each couplet now sits in a soft card.
The dictionary and the reading settings open beside the poem rather than over
it, so nothing you are reading is covered.
Pages fill in where they load instead of blanking the screen, and Home and the
reading settings stay reachable while they do.
Tapping a word no longer carries the comma or question mark after it into the
lookup.
A reading keeps playing when you scroll down the poem, and a seek bar moves it
back and forth.
@@ -1,17 +0,0 @@
تبلت‌ها و گوشی‌های تاشوی باز حالا ستونی خوانده می‌شوند: شاعران در سمت راست، سپس
یک ستون برای هر لایهٔ کتاب باز، و شعر در هر فضایی که می‌ماند. هر ستون جداگانه
پیمایش می‌شود و با خواندن باریک‌تر می‌شود تا شعر میانه را نگه دارد. دکمه‌ای در
نوار بالا آن‌ها را جمع می‌کند.
هر جا جا باشد، دو مصرع یک بیت در یک سطر می‌نشینند، همان‌گونه که گنجور روی رایانه
می‌چیند. هر بیت اکنون در کارتی ملایم می‌نشیند.
لغت‌نامه و تنظیمات خواندن کنار شعر باز می‌شوند، نه رویش، تا چیزی از آنچه
می‌خوانید پوشیده نشود.
صفحه‌ها همان‌جا که بار می‌شوند پر می‌شوند و صفحه سفید نمی‌ماند؛ خانه و تنظیمات
خواندن در همان حال هم در دسترس‌اند.
ضربه بر یک واژه دیگر ویرگول یا علامت پرسش پس از آن را به جست‌وجو نمی‌برد.
خوانش با پیمایش شعر قطع نمی‌شود، و نواری تازه آن را پس و پیش می‌برد.
@@ -1,17 +0,0 @@
ٹیبلٹ اور کھلے فولڈ ایبل اب ستونوں میں پڑھے جاتے ہیں: شعرا دائیں طرف، پھر کھلی
کتاب کی ہر سطح کے لیے ایک ستون، اور باقی جگہ میں کلام۔ ہر ستون الگ سکرول ہوتا ہے
اور پڑھتے وقت تنگ ہو جاتا ہے تاکہ کلام درمیان میں رہے۔ اوپر کی پٹی کا ایک بٹن
انہیں سمیٹ دیتا ہے۔
جہاں گنجائش ہو، شعر کے دونوں مصرعے ایک ہی سطر میں آتے ہیں، جیسے گنجور کمپیوٹر پر
دکھاتا ہے۔ ہر شعر اب ایک ہلکے کارڈ میں بیٹھتا ہے۔
لغت نامہ اور مطالعے کی ترتیبات کلام کے ساتھ کھلتی ہیں، اُس پر نہیں، تاکہ جو آپ
پڑھ رہے ہیں وہ ڈھکا نہ جائے۔
صفحات وہیں بھرتے ہیں جہاں لوڈ ہوتے ہیں، خالی اسکرین نہیں آتی؛ ہوم اور مطالعے کی
ترتیبات اُس دوران بھی دستیاب رہتی ہیں۔
کسی لفظ پر ٹیپ اب اُس کے بعد والا کوما یا سوالیہ نشان ساتھ نہیں لے جاتی۔
سکرول کرنے پر تلاوت بند نہیں ہوتی، اور ایک نئی سیک بار اُسے آگے پیچھے کرتی ہے۔