Skip to content

Commit

Permalink
Prepare to release v0.12.0
Browse files Browse the repository at this point in the history
  • Loading branch information
saket committed Jul 13, 2024
1 parent 617b369 commit 5b8ee12
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 13 deletions.
42 changes: 42 additions & 0 deletions docs/zoomable/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ A `Modifier` for handling pan & zoom gestures, designed to be shared across all
- Haptic feedback for over/under zoom
- Compatibility with nested scrolling
- Click listeners
- [Keyboard and mouse shortcuts](#keyboard-and-mouse-shortcuts)

### Installation

Expand Down Expand Up @@ -73,6 +74,15 @@ Modifier.zoomable(
)
```

The default behavior of toggling between minimum and maximum zoom levels on double-clicks can be overridden by using
the `onDoubleClick` parameter:

```kotlin
Modifier.zoomable(
onDoubleClick = { state, centroid -> … },
)
```

### Applying gesture transformations

When pan & zoom gestures are received, `Modifier.zoomable()` automatically applies their resulting `scale` and `translation` onto your content using `Modifier.graphicsLayer()`.
Expand All @@ -98,3 +108,35 @@ Text(
}
)
```

# Keyboard and Mouse shortcuts

`ZoomableImage()` can observe keyboard and mouse shortcuts for panning and zooming when it is focused, either by the
user or using a `FocusRequester`:

```kotlin hl_lines="6 11"
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
// Automatically request focus when the image is displayed. This assumes there
// is only one zoomable image present in the hierarchy. If you're displaying
// multiple images in a pager, apply this only for the active page.
focusRequester.requestFocus()
}

Box(
Modifier
.focusRequester(focusRequester)
.zoomable(),
)
```

By default, the following shortcuts are recognized. These can be customized (or disabled) by passing a
custom `HardwareShortcutsSpec` to `rememberZoomableState()`.

| | Android | Desktop |
|-----------|--------------------|-----------------------|
| Zoom in | `Control` + `=` | `Meta` + `=` |
| Zoom out | `Control` + `-` | `Meta` + `-` |
| Pan | Arrow keys | Arrow keys |
| Extra pan | `Alt` + arrow keys | `Option` + arrow keys |

22 changes: 22 additions & 0 deletions docs/zoomable/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ LaunchedEffect(isZoomedOut) {
}
```

### Controlling pan & zoom

```kotlin
val state = rememberZoomableState()
Box(
Modifier.zoomable(state)
)

Button(onClick = { state.zoomBy(zoomFactor = 1.2f) }) {
Text("+")
}
Button(onClick = { state.zoomBy(zoomFactor = 1 / 1.2f) }) {
Text("-")
}
Button(onClick = { state.panBy(offset = 50.dp) }) {
Text(">")
}
Button(onClick = { state.panBy(offset = -50.dp) }) {
Text("<")
}
```

### Resetting zoom
`Modifier.zoomable()` will automatically retain its pan & zoom across state restorations. You may want to prevent this in lazy layouts such as a `Pager()`, where each page is restored every time it becomes visible.

Expand Down
2 changes: 1 addition & 1 deletion docs/zoomableimage/custom-image-loaders.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Custom image loaders

In its essence, `ZoomableImage` is simply an abstraction over an image loading library. If your preferred library isn't supported by `telephoto` out of the box, you can create your own by extending `ZoomableImageSource`.
In its essence, `ZoomableImage` is simply an abstraction over an image loading library. If your preferred library isn't supported by `telephoto` out of the box, you can create your own by implementing `ZoomableImageSource`.

```kotlin
@Composable
Expand Down
72 changes: 64 additions & 8 deletions docs/zoomableimage/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ A _drop-in_ replacement for async `Image()` composables featuring support for pa
- Haptic feedback for over/under zoom
- Compatibility with nested scrolling
- Click listeners
- [Keyboard and mouse shortcuts](#keyboard-and-mouse-shortcuts)

### Installation

Expand Down Expand Up @@ -52,12 +53,8 @@ For complex scenarios, `ZoomableImage` can also take full image requests:
model = ImageRequest.Builder(LocalContext.current)
.data("https://example.com/image.jpg")
.listener(
remember {
object : ImageRequest.Listener {
override fun onSuccess(…) {}
override fun onError(…) {}
}
}
onSuccess = { … },
onError = { … },
)
.crossfade(1_000)
.memoryCachePolicy(CachePolicy.DISABLED)
Expand Down Expand Up @@ -178,7 +175,7 @@ Unlike `Image()`, `ZoomableImage` can pan images even when they're cropped. This
For detecting double clicks, `ZoomableImage` consumes all tap gestures making it incompatible with `Modifier.clickable()` and `Modifier.combinedClickable()`. As an alternative, its `onClick` and `onLongClick` parameters can be used.

=== "Coil"
```kotlin
```kotlin hl_lines="4-5"
ZoomableAsyncImage(
modifier = Modifier.clickable { error("This will not work") },
model = "https://example.com/image.jpg",
Expand All @@ -187,7 +184,7 @@ For detecting double clicks, `ZoomableImage` consumes all tap gestures making it
)
```
=== "Glide"
```kotlin
```kotlin hl_lines="4-5"
ZoomableGlideImage(
modifier = Modifier.clickable { error("This will not work") },
model = "https://example.com/image.jpg",
Expand All @@ -196,6 +193,65 @@ For detecting double clicks, `ZoomableImage` consumes all tap gestures making it
)
```

The default behavior of toggling between minimum and maximum zoom levels on double-clicks can be overridden by using the `onDoubleClick` parameter:

=== "Coil"
```kotlin
ZoomableAsyncImage(
model = "https://example.com/image.jpg",
onDoubleClick = { state, centroid -> … },
)
```
=== "Glide"
```kotlin
ZoomableGlideImage(
model = "https://example.com/image.jpg",
onDoubleClick = { state, centroid -> … },
)
```

```kotlin
Modifier.zoomable(
onDoubleClick = { state, centroid -> … }
)
```

# Keyboard and Mouse shortcuts

`ZoomableImage()` can observe keyboard and mouse shortcuts for panning and zooming when it is focused, either by the user or using a `FocusRequester`:

```kotlin hl_lines="6"
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
// Automatically request focus when the image is displayed. This assumes there
// is only one zoomable image present in the hierarchy. If you're displaying
// multiple images in a pager, apply this only for the active page.
focusRequester.requestFocus()
}
```

=== "Coil"
```kotlin hl_lines="2"
ZoomableAsyncImage(
modifier = Modifier.focusRequester(focusRequester),
model = "https://example.com/image.jpg",
)
```
=== "Glide"
```kotlin hl_lines="2"
ZoomableGlideImage(
modifier = Modifier.focusRequester(focusRequester),
model = "https://example.com/image.jpg",
)
```

By default, the following shortcuts are recognized. These can be customized (or disabled) by passing a custom `HardwareShortcutsSpec` to `rememberZoomableState()`.

|-----------|--------------------|
| Zoom in | `Control` + `=` |
| Zoom out | `Control` + `-` |
| Pan | Arrow keys |
| Extra pan | `Alt` + arrow keys |

### Sharing hoisted state

Expand Down
1 change: 1 addition & 0 deletions docs/zoomableimage/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Modifier.zoomable()
- [Observing pan & zoom](../zoomable/recipes.md#observing-pan-zoom)
- [Controlling pan & zoom](../zoomable/recipes.md#controlling-pan-zoom)
- [Resetting zoom](../zoomable/recipes.md#resetting-zoom)

### Setting zoom limits
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ kotlin.mpp.androidSourceSetLayoutVersion=2
android.enableBuildConfigAsBytecode=true

GROUP=me.saket.telephoto
VERSION_NAME=0.12.0-SNAPSHOT
VERSION_NAME=0.12.0

SONATYPE_HOST=DEFAULT
RELEASE_SIGNING_ENABLED=true
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ plugins:

extra:
versions:
telephoto: '0.11.2' # Env var for the latest version on maven.
telephoto: '0.12.0' # Env var for the latest version on maven.

nav:
- 'Overview': index.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ import me.saket.telephoto.subsamplingimage.SubSamplingImage
import me.saket.telephoto.subsamplingimage.rememberSubSamplingImageState
import me.saket.telephoto.zoomable.internal.FocusForwarder
import me.saket.telephoto.zoomable.internal.PlaceholderBoundsProvider
import me.saket.telephoto.zoomable.internal.focusable
import me.saket.telephoto.zoomable.internal.focusForwarder
import me.saket.telephoto.zoomable.internal.focusable
import me.saket.telephoto.zoomable.internal.scaledToMatch

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import dev.drewhamilton.poko.Poko
* Describes how keyboard and mouse shortcuts are handled. When [enabled], [Modifier.zoomable][zoomable]'s
* content will participate in the focus system to receive hardware events.
*
* Keep in mind that hardware shortcuts will not work until your zoomable content is focused.
* Keep in mind that hardware shortcuts will only work when your zoomable content is focused.
* To do this automatically, use a [FocusRequester]:
*
* ```
* val focusRequester = remember { FocusRequester() }
* LaunchedEffect(Unit) {
* // Automatically request focus when the image is displayed. This assumes there
* // is only one zoomable image present in the hierarchy. If you're displaying
* // multiple images in a pager, apply this only for the active page.
* focusRequester.requestFocus()
* }
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import me.saket.telephoto.zoomable.HardwareShortcutDetector.ShortcutEvent.ZoomDi
import kotlin.math.absoluteValue

internal object DefaultHardwareShortcutDetector : HardwareShortcutDetector {

override fun detectKey(event: KeyEvent): ShortcutEvent? {
// Note for self: Some devices/peripherals have dedicated zoom buttons that map to Key.ZoomIn
// and Key.ZoomOut. Examples include: Samsung Galaxy Camera, a motorcycle handlebar controller.
Expand Down

0 comments on commit 5b8ee12

Please sign in to comment.