Turn on the built-in gesture that swaps the images of two blocks, and choose the parent and the center of a block created from an asset.
The examples below use a page with two image blocks side by side:
val scene = engine.scene.create()
val page = engine.block.create(DesignBlockType.Page)engine.block.setWidth(page, value = 800F)engine.block.setHeight(page, value = 600F)engine.block.appendChild(parent = scene, child = page)
// A block lifts or receives an image when it has an image fill, is not a sticker, and its// `fill/change` scope is allowed. Give each block a rectangle shape and an image fill.fun addImage( uri: String, x: Float,) { val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block = block, shape = engine.block.createShape(ShapeType.Rect)) val fill = engine.block.createFill(FillType.Image) engine.block.setString(block = fill, property = "fill/image/imageFileURI", value = uri) engine.block.setFill(block = block, fill = fill) engine.block.setWidth(block = block, value = 300F) engine.block.setHeight(block = block, value = 300F) engine.block.setPositionX(block = block, value = x) engine.block.setPositionY(block = block, value = 150F) engine.block.appendChild(parent = page, child = block)}
addImage(uri = "https://img.ly/static/ubq_samples/sample_1.jpg", x = 50F)addImage(uri = "https://img.ly/static/ubq_samples/sample_2.jpg", x = 450F)The swap gesture#
Press and hold an image block to lift its image. Drag it onto another image block, which is highlighted as the drop target, and release to exchange the two images. The engine runs the whole gesture and writes a single undo step.
Two settings control it:
// Let users exchange images by dragging one image block onto another.engine.editor.setSettingBoolean("dragToSwapFills/enabled", value = true)
// How long the pointer stays pressed and still before the image lifts.// Use 0F for fixed layouts, where the image lifts at once.engine.editor.setSettingFloat("dragToSwapFills/longPressDurationMs", value = 500F)dragToSwapFills/enabled is off by default. dragToSwapFills/longPressDurationMs defaults to 500 milliseconds. Set it to 0 for fixed layouts, where the image lifts at once.
The Design, Photo, Video, Postcard and Apparel Starter Kits turn the gesture on.
A block can lift or receive an image when it has an image fill, is not a sticker or an animated sticker, and its fill/change scope is allowed. A block whose editor/select scope is denied takes no part either: a press or a release over it reaches the block below it, as a tap does. A page never takes part in a swap. The gesture runs only in the transform edit mode.
The engine view draws the preview that follows the finger, and it gives haptic feedback when the image lifts. When the image cannot load, the preview shows a plain card. There is nothing to render and nothing to cancel by hand. The engine ends the gesture when the touch is cancelled. engine.pause() and engine.unbind() also end an active lift.
Placing an asset#
AssetPlacement tells the engine which parent a new block joins and where its center sits. Register a source and fetch one asset from it:
// A source with its own applyAsset function ignores the placement, so this one has none.engine.asset.addLocalSource( sourceId = PLACEMENT_SOURCE_ID, supportedMimeTypes = listOf(MimeType.JPEG.key),)engine.asset.addAsset( sourceId = PLACEMENT_SOURCE_ID, asset = AssetDefinition( id = PLACEMENT_ASSET_ID, label = mapOf("en" to "Sample image"), meta = mapOf( "uri" to PLACEMENT_ASSET_URI, "thumbUri" to PLACEMENT_ASSET_URI, "mimeType" to MimeType.JPEG.key, "kind" to "image", "width" to "1667", "height" to "2500", "blockType" to DesignBlockType.Graphic.key, "fillType" to FillType.Image.key, "shapeType" to ShapeType.Rect.key, ), ),)
val asset = engine.asset.fetchAsset( sourceId = PLACEMENT_SOURCE_ID, assetId = PLACEMENT_ASSET_ID,) ?: error("Could not fetch the asset: $PLACEMENT_ASSET_ID")Then apply the asset at a placement:
// Add the asset to `page`, centered at (200, 300) relative to `page`.// Both coordinates must be finite.val placedBlock = engine.asset.applyAssetSourceAsset( sourceId = PLACEMENT_SOURCE_ID, asset = asset, placement = AssetPlacement(parent = page, center = PointF(200F, 300F)),)Log.i(TAG, "Placed block $placedBlock")
// A null parent adds the block to the current page.val defaultPlacedBlock = engine.asset.defaultApplyAsset( asset = asset, placement = AssetPlacement(parent = null, center = PointF(600F, 300F)),)Log.i(TAG, "Placed block $defaultPlacedBlock")applyAssetSourceAsset runs the source’s own applyAsset function. A source that defines one ignores the placement, so register no applyAsset when you want to place the block. defaultApplyAsset skips the source’s function and uses the placement, with the exceptions below.
An asset that holds a transform preset or a scene replaces the page or the scene, so it uses neither the parent nor the center. An archived block collection keeps its own positions and uses only the parent.
The center is in design units relative to parent, and both coordinates must be finite. A null parent adds the block to the current page. A null center lets the engine place the block itself.
The engine view takes no drags from outside the canvas. To add an asset at a point the user picks, apply it with AssetPlacement.
API Reference#
Methods#
| Method | Description |
|---|---|
engine.scene.create() |
Create an empty scene |
engine.block.create(blockType=_) |
Create a page or a graphic block |
engine.block.setWidth(block=_, value=_) |
Set the width of a block |
engine.block.setHeight(block=_, value=_) |
Set the height of a block |
engine.block.appendChild(parent=_, child=_) |
Add a block to a parent |
engine.block.createShape(type=_) |
Create a shape |
engine.block.setShape(block=_, shape=_) |
Give a graphic block its shape |
engine.block.createFill(fillType=_) |
Create a fill |
engine.block.setString(block=_, property="fill/image/imageFileURI", value=_) |
Set the image of an image fill |
engine.block.setFill(block=_, fill=_) |
Give a block its fill |
engine.block.setPositionX(block=_, value=_) |
Set the horizontal position of a block |
engine.block.setPositionY(block=_, value=_) |
Set the vertical position of a block |
engine.editor.setSettingBoolean(keypath="dragToSwapFills/enabled", value=_) |
Turn the swap gesture on or off |
engine.editor.setSettingFloat(keypath="dragToSwapFills/longPressDurationMs", value=_) |
Set how long the press lasts before the image lifts |
engine.asset.addLocalSource(sourceId=_, supportedMimeTypes=_) |
Register a local asset source |
engine.asset.addAsset(sourceId=_, asset=_) |
Add an asset to a local source |
engine.asset.fetchAsset(sourceId=_, assetId=_) |
Fetch one asset from a source |
engine.asset.applyAssetSourceAsset(sourceId=_, asset=_, placement=_) |
Apply an asset through its source’s function, at a placement |
engine.asset.defaultApplyAsset(asset=_, placement=_) |
Apply an asset with the default implementation, at a placement |
Properties#
| Setting | Type | Default | Description |
|---|---|---|---|
dragToSwapFills/enabled |
Boolean | false |
Turns the press-and-hold swap gesture on |
dragToSwapFills/longPressDurationMs |
Float | 500 |
Hold in milliseconds before the image lifts. 0 lifts at once, which suits fixed layouts |
Key types#
| Type | Property | Type | Description |
|---|---|---|---|
AssetPlacement |
parent |
DesignBlock? |
The block the new block is added to; null is the current page |
AssetPlacement |
center |
PointF? |
The center relative to parent; null lets the engine place it |