commonMain.me.saket.telephoto.zoomable.ZoomableContentTransformation.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zoomable-desktop Show documentation
Show all versions of zoomable-desktop Show documentation
A Modifier for making anything zoomable.
The newest version!
package me.saket.telephoto.zoomable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ScaleFactor
import me.saket.telephoto.zoomable.internal.Zero
/**
* Graphics transformations generated by [Modifier.zoomable] when pan & zoom gestures are received.
*
* By default, these are automatically applied by `Modifier.zoomable()` to its content, but this
* can be disabled using [ZoomableState.autoApplyTransformations] if you prefer applying them in
* a bespoke manner.
*/
@Immutable
interface ZoomableContentTransformation {
/**
* Whether this object contains non-placeholder values. This will be false when
* [Modifier.zoomable] is initializing and sufficient information to position
* and show its content is not yet available.
*
* Until specified transformations are received, zoomable content should stay hidden.
* This is done automatically if [ZoomableState.autoApplyTransformations] is `true`.
*/
val isSpecified: Boolean
/**
* The size of the zoomable content that is currently known to [Modifier.zoomable].
* This is calculated using the value given to [ZoomableState.setContentLocation] and
* does not account for the zoom and pan transformations. Useful for synchronizing
* other elements with the zoomable content.
*/
val contentSize: Size
/**
* The scale of the zoomable content along the x and y axes, generated in response
* to zoom gestures. This value represents the scaling factor that should be applied
* to the content to achieve the desired zoom level.
*
* Values on both the axes can be less than 0 if the content size is larger than
* its viewport size.
*/
val scale: ScaleFactor
val scaleMetadata: ScaleMetadata
/**
* The rotation of the zoomable content around the z-axis. This parameter is currently
* unused and defaults to `0`, but may be used in the future if [Modifier.zoomable]
* adds support for rotation gestures.
*/
val rotationZ: Float
/**
* The translation of the zoomable content along the x and y axes, generated in
* response to pan gestures. This value represents the amount by which the content
* should be translated to achieve the desired pan position.
*/
val offset: Offset
/**
* Offset percentage along the x and y axis for which [scale] and [offset] must be applied.
*/
val transformOrigin: TransformOrigin get() = TransformOrigin.Zero
/**
* Central point around which the last pan/zoom gesture was made with respect to the
* viewport bounds. Will be `null` if the content hasn't been interacted with yet.
*/
val centroid: Offset?
/** Inverse of [isSpecified]. */
val isUnspecified: Boolean get() = !isSpecified
interface ScaleMetadata {
/**
* The default scale needed to position the content within its layout bounds with
* respect to [ZoomableState.contentScale].
*/
val initialScale: ScaleFactor
/**
* Scale applied by the user using zoom gestures. The final scale of the content
* is calculated using [initialScale] x [userZoom], and will always be lower or equal
* to [ZoomSpec.maxZoomFactor].
*
* For example, a `userZoom` of `1.5f` indicates that the content is zoomed 150% from its
* [initialScale].
*/
val userZoom: Float
}
}
@Stable
internal fun Modifier.applyTransformation(transformation: () -> ZoomableContentTransformation): Modifier {
return graphicsLayer {
@Suppress("NAME_SHADOWING")
val transformation = transformation()
scaleX = transformation.scale.scaleX
scaleY = transformation.scale.scaleY
rotationZ = transformation.rotationZ
translationX = transformation.offset.x
translationY = transformation.offset.y
transformOrigin = transformation.transformOrigin
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy