Open reading settings in the left-hand panel on large screens
On a tablet or an unfolded foldable the gear now opens reading settings as a panel on the left, where the dictionary opens, instead of a bottom sheet. The page stays in view beside it, so a change of theme, font, weight or size shows on the poem as it is made. The sheet and the panel share one ReadingSettingsContent; phones keep the sheet. Adds wireframe 8 and notes in the design docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RhmDdrN5hgWrgRcFLAMsV6
This commit is contained in:
parent
f0c1dd5541
commit
8451cc45fb
@ -31,7 +31,7 @@ The whole interface lays out and navigates right-to-left, whichever UI language
|
|||||||
On a tablet or an unfolded foldable (600dp and wider) the poets become a narrow column on the right,
|
On a tablet or an unfolded foldable (600dp and wider) the poets become a narrow column on the right,
|
||||||
with the open poet's books, chapters and poems in columns beside it and the page in the rest of the
|
with the open poet's books, chapters and poems in columns beside it and the page in the rest of the
|
||||||
screen. The columns narrow while a poem is open and can be hidden for a reader view. The dictionary
|
screen. The columns narrow while a poem is open and can be hidden for a reader view. The dictionary
|
||||||
opens in a panel to the left of the poem instead of over it. Phones are unchanged.
|
and reading settings open in a panel to the left of the poem instead of over it. Phones are unchanged.
|
||||||
|
|
||||||
## Fonts
|
## Fonts
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
package com.ganjoor.android.ui
|
package com.ganjoor.android.ui
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
|
import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.expandHorizontally
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.shrinkHorizontally
|
||||||
import androidx.compose.animation.EnterTransition
|
import androidx.compose.animation.EnterTransition
|
||||||
import androidx.compose.animation.ExitTransition
|
import androidx.compose.animation.ExitTransition
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
@ -102,6 +109,10 @@ fun GanjoorApp() {
|
|||||||
val wide by rememberUpdatedState(maxWidth >= 600.dp)
|
val wide by rememberUpdatedState(maxWidth >= 600.dp)
|
||||||
// Up to three list columns beside the poets; below this, only the newest one.
|
// Up to three list columns beside the poets; below this, only the newest one.
|
||||||
val expanded by rememberUpdatedState(maxWidth >= 840.dp)
|
val expanded by rememberUpdatedState(maxWidth >= 840.dp)
|
||||||
|
// On a large screen reading settings open in a panel on the left, where the dictionary
|
||||||
|
// opens, so the page stays in view and shows each change as it is made.
|
||||||
|
Row(Modifier.fillMaxSize()) {
|
||||||
|
Box(Modifier.weight(1f)) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = nav,
|
navController = nav,
|
||||||
startDestination = PoetsRoute,
|
startDestination = PoetsRoute,
|
||||||
@ -226,8 +237,19 @@ fun GanjoorApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (wide) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = settingsOpen,
|
||||||
|
enter = expandHorizontally() + fadeIn(),
|
||||||
|
exit = shrinkHorizontally() + fadeOut(),
|
||||||
|
) {
|
||||||
|
ReadingSettingsPanel(onDismiss = { settingsOpen = false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Inside the provider: the sheet reads LocalOpenAbout, so it has to be in scope.
|
// Inside the provider: the sheet reads LocalOpenAbout, so it has to be in scope.
|
||||||
if (settingsOpen) ReadingSettingsSheet(onDismiss = { settingsOpen = false })
|
if (settingsOpen && !wide) ReadingSettingsSheet(onDismiss = { settingsOpen = false })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,32 @@
|
|||||||
package com.ganjoor.android.ui
|
package com.ganjoor.android.ui
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@ -58,11 +66,53 @@ fun ReadingSettingsAction() {
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ReadingSettingsSheet(onDismiss: () -> Unit) {
|
fun ReadingSettingsSheet(onDismiss: () -> Unit) {
|
||||||
|
ModalBottomSheet(onDismissRequest = onDismiss) {
|
||||||
|
ReadingSettingsContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The same settings as [ReadingSettingsSheet], as a panel on the left of a large screen — the
|
||||||
|
* place the dictionary opens too. The page stays in view beside it, so a change of theme, font
|
||||||
|
* or size shows on the poem itself as it is made. The cross, or Back, closes it.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ReadingSettingsPanel(onDismiss: () -> Unit) {
|
||||||
|
BackHandler(onBack = onDismiss)
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||||
|
modifier = Modifier.width(360.dp).fillMaxHeight(),
|
||||||
|
) {
|
||||||
|
Column(Modifier.statusBarsPadding()) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.heightIn(min = 56.dp)
|
||||||
|
.padding(start = 20.dp, end = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.reading_settings),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
IconButton(onClick = onDismiss) {
|
||||||
|
Icon(Icons.Default.Close, stringResource(R.string.close))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalDivider(modifier = Modifier.padding(bottom = 8.dp))
|
||||||
|
ReadingSettingsContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ReadingSettingsContent() {
|
||||||
val settings = LocalSettings.current
|
val settings = LocalSettings.current
|
||||||
val prefs = settings.value
|
val prefs = settings.value
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
ModalBottomSheet(onDismissRequest = onDismiss) {
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
// Inset first, then scroll: the viewport has to stop above the nav bar,
|
// Inset first, then scroll: the viewport has to stop above the nav bar,
|
||||||
@ -148,7 +198,6 @@ fun ReadingSettingsSheet(onDismiss: () -> Unit) {
|
|||||||
Text(stringResource(R.string.about))
|
Text(stringResource(R.string.about))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@ -60,4 +60,4 @@ Poet portraits come from Ganjoor and are cropped into a circle on `secondary-con
|
|||||||
Material Icons in the filled style, at 24px, tinted with colour roles (see the Icons group). Use the standard Android share glyph wherever there is sharing, because people recognise it before reading the label. The brand mark is the eight-pointed khatam star (see Logos). There is no wordmark: گنجور is set in Naskh.
|
Material Icons in the filled style, at 24px, tinted with colour roles (see the Icons group). Use the standard Android share glyph wherever there is sharing, because people recognise it before reading the label. The brand mark is the eight-pointed khatam star (see Logos). There is no wordmark: گنجور is set in Naskh.
|
||||||
|
|
||||||
## Large screens
|
## Large screens
|
||||||
Tablets and unfolded foldables use **ColumnBrowser**. Tapping a poet collapses the home grid into a narrow poets column on the far right (portrait with the name underneath). To its left come the poet's books, then the chapters or poems of the open book, each column scrolling on its own. Older columns narrow as new ones open, so the poem stays near the middle, and a top-bar button hides the columns when the reader wants to focus on the poem. Phones are unchanged.
|
Tablets and unfolded foldables use **ColumnBrowser**. Tapping a poet collapses the home grid into a narrow poets column on the far right (portrait with the name underneath). To its left come the poet's books, then the chapters or poems of the open book, each column scrolling on its own. Older columns narrow as new ones open, so the poem stays near the middle, and a top-bar button hides the columns when the reader wants to focus on the poem. The dictionary and reading settings open in a panel on the left, beside the poem rather than over it. Phones are unchanged.
|
||||||
|
|||||||
@ -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/`.
|
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).
|
||||||
|
|
||||||
## Window sizes
|
## Window sizes
|
||||||
|
|
||||||
@ -46,6 +46,13 @@ On large screens, tapping a word opens **WordPanel** on the left instead of the
|
|||||||
- The tapped word stays highlighted in the verse (`secondary-container` / `on-secondary-container`), on phones too.
|
- The tapped word stays highlighted in the verse (`secondary-container` / `on-secondary-container`), on phones too.
|
||||||
- ✕ or Back closes the panel.
|
- ✕ or Back closes the panel.
|
||||||
|
|
||||||
|
## Reading settings
|
||||||
|
|
||||||
|
On large screens the gear opens **ReadingSettingsPanel** on the left, where the dictionary opens, instead of the bottom sheet:
|
||||||
|
- It has the same width and surface as the dictionary panel, with a "Reading settings" header and a ✕ button. The settings and their order are the same as in the sheet.
|
||||||
|
- The page stays in view beside the panel, so a change of theme, font, weight or size shows on the poem as it's made.
|
||||||
|
- ✕ or Back closes it. Phones keep the bottom sheet.
|
||||||
|
|
||||||
## Loading
|
## Loading
|
||||||
|
|
||||||
Nothing covers the whole screen while it loads, on phones as well:
|
Nothing covers the whole screen while it loads, on phones as well:
|
||||||
|
|||||||
@ -7,3 +7,4 @@ The Material bottom sheet behind the gear icon: theme, OLED, font, weight, text
|
|||||||
- Order: Theme chips → OLED toggle → Font chips → Weight chips → Text size slider (14–40, steps of 2) → **live preview** of a hemistich in the chosen reading style → Language chips → Offline mode → Show summaries → AI assistant, About & licences as TextButtons.
|
- Order: Theme chips → OLED toggle → Font chips → Weight chips → Text size slider (14–40, steps of 2) → **live preview** of a hemistich in the chosen reading style → Language chips → Offline mode → Show summaries → AI assistant, About & licences as TextButtons.
|
||||||
- Every group has a **SectionLabel**. Changes apply immediately; there is no Save.
|
- Every group has a **SectionLabel**. Changes apply immediately; there is no Save.
|
||||||
- Changing language recreates the activity.
|
- Changing language recreates the activity.
|
||||||
|
- On tablets and unfolded foldables the same settings open as a panel on the left instead (`ReadingSettingsPanel`, see ColumnBrowser), so the poem stays in view while they change.
|
||||||
|
|||||||
BIN
design/wireframes/8-settings.png
Normal file
BIN
design/wireframes/8-settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
Loading…
Reference in New Issue
Block a user