-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: break up style manager (#176)
- Loading branch information
Showing
18 changed files
with
341 additions
and
331 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 33 additions & 7 deletions
40
...-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/engine/ImageManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,56 @@ | ||
package dev.sargunv.maplibrecompose.compose.engine | ||
|
||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.DisposableEffect | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.graphics.ImageBitmap | ||
import dev.sargunv.maplibrecompose.core.expression.Expression | ||
import dev.sargunv.maplibrecompose.core.expression.ExpressionValue | ||
import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.cast | ||
import dev.sargunv.maplibrecompose.core.expression.ResolvedValue | ||
|
||
internal class ImageManager(private val styleManager: StyleManager) { | ||
internal class ImageManager(private val node: StyleNode) { | ||
private val idMap = IncrementingIdMap<ImageBitmap>("image") | ||
|
||
private val counter = | ||
ReferenceCounter<ImageBitmap>( | ||
onZeroToOne = { image -> | ||
val id = idMap.addId(image) | ||
styleManager.logger?.i { "Adding image $id" } | ||
styleManager.style.addImage(id, image) | ||
node.logger?.i { "Adding image $id" } | ||
node.style.addImage(id, image) | ||
}, | ||
onOneToZero = { image -> | ||
val id = idMap.removeId(image) | ||
styleManager.logger?.i { "Removing image $id" } | ||
styleManager.style.removeImage(id) | ||
node.logger?.i { "Removing image $id" } | ||
node.style.removeImage(id) | ||
}, | ||
) | ||
|
||
fun addReference(image: ImageBitmap): String { | ||
private fun addReference(image: ImageBitmap): String { | ||
counter.increment(image) | ||
return idMap.getId(image) | ||
} | ||
|
||
fun removeReference(image: ImageBitmap) { | ||
private fun removeReference(image: ImageBitmap) { | ||
counter.decrement(image) | ||
} | ||
|
||
@Composable | ||
internal fun <T : ExpressionValue> resolveImages( | ||
expr: Expression<T> | ||
): Expression<ResolvedValue<T>> { | ||
DisposableEffect(expr) { | ||
onDispose { expr.visitLeaves { if (it is ImageBitmap) removeReference(it) } } | ||
} | ||
return remember(expr) { | ||
expr | ||
.mapLeaves { | ||
when (it) { | ||
is ImageBitmap -> addReference(it) | ||
else -> it | ||
} | ||
} | ||
.cast() | ||
} | ||
} | ||
} |
135 changes: 135 additions & 0 deletions
135
...-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/engine/LayerManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package dev.sargunv.maplibrecompose.compose.engine | ||
|
||
import dev.sargunv.maplibrecompose.compose.layer.Anchor | ||
import dev.sargunv.maplibrecompose.core.layer.Layer | ||
|
||
internal class LayerManager(private val styleNode: StyleNode) { | ||
private val baseLayers = styleNode.style.getLayers().associateBy { it.id } | ||
|
||
private val userLayers = mutableListOf<LayerNode<*>>() | ||
|
||
// special handling for Replace anchors | ||
private val replacedLayers = mutableMapOf<Anchor.Replace, Layer>() | ||
private val replacementCounters = mutableMapOf<Anchor.Replace, Int>() | ||
|
||
internal fun addLayer(node: LayerNode<*>, index: Int) { | ||
require(node.layer.id !in baseLayers) { | ||
"Layer ID '${node.layer.id}' already exists in base style" | ||
} | ||
node.anchor.validate() | ||
styleNode.logger?.i { | ||
"Queuing layer ${node.layer.id} for addition at anchor ${node.anchor}, index $index" | ||
} | ||
userLayers.add(index, node) | ||
} | ||
|
||
internal fun removeLayer(node: LayerNode<*>, oldIndex: Int) { | ||
userLayers.removeAt(oldIndex) | ||
|
||
// special handling for Replace anchors | ||
// restore the original before removing if this layer was the last replacement | ||
val anchor = node.anchor | ||
if (anchor is Anchor.Replace) { | ||
val count = replacementCounters.getValue(anchor) - 1 | ||
if (count > 0) replacementCounters[anchor] = count | ||
else { | ||
replacementCounters.remove(anchor) | ||
styleNode.logger?.i { "Restoring layer ${anchor.layerId}" } | ||
styleNode.style.addLayerBelow(node.layer.id, replacedLayers.remove(anchor)!!) | ||
} | ||
} | ||
|
||
styleNode.logger?.i { "Removing layer ${node.layer.id}" } | ||
styleNode.style.removeLayer(node.layer) | ||
node.added = false | ||
} | ||
|
||
internal fun moveLayer(node: LayerNode<*>, oldIndex: Int, index: Int) { | ||
styleNode.logger?.i { "Moving layer ${node.layer.id} from $oldIndex to $index" } | ||
removeLayer(node, oldIndex) | ||
addLayer(node, index) | ||
styleNode.logger?.i { "Done moving layer ${node.layer.id}" } | ||
} | ||
|
||
internal fun applyChanges() { | ||
val tailLayerIds = mutableMapOf<Anchor, String>() | ||
val missedLayers = mutableMapOf<Anchor, MutableList<LayerNode<*>>>() | ||
|
||
userLayers.forEach { node -> | ||
val layer = node.layer | ||
val anchor = node.anchor | ||
|
||
if (node.added && anchor in missedLayers) { | ||
// we found an existing head; let's add the missed layers | ||
val layersToAdd = missedLayers.remove(anchor)!! | ||
layersToAdd.forEach { missedLayer -> | ||
styleNode.logger?.i { "Adding layer ${missedLayer.layer.id} below ${layer.id}" } | ||
styleNode.style.addLayerBelow(layer.id, missedLayer.layer) | ||
missedLayer.markAdded() | ||
} | ||
} | ||
|
||
if (!node.added) { | ||
// we found a layer to add; let's try to add it, or queue it up until we find a head | ||
tailLayerIds[anchor]?.let { tailLayerId -> | ||
styleNode.logger?.i { "Adding layer ${layer.id} below $tailLayerId" } | ||
styleNode.style.addLayerAbove(tailLayerId, layer) | ||
node.markAdded() | ||
} ?: missedLayers.getOrPut(anchor) { mutableListOf() }.add(node) | ||
} | ||
|
||
// update the tail | ||
if (node.added) tailLayerIds[anchor] = layer.id | ||
} | ||
|
||
// anything left in missedLayers is a new anchor | ||
missedLayers.forEach { (anchor, nodes) -> | ||
// let's initialize the anchor with one layer | ||
val tail = nodes.removeLast() | ||
styleNode.logger?.i { "Initializing anchor $anchor with layer ${tail.layer.id}" } | ||
when (anchor) { | ||
is Anchor.Top -> styleNode.style.addLayer(tail.layer) | ||
is Anchor.Bottom -> styleNode.style.addLayerAt(0, tail.layer) | ||
is Anchor.Above -> styleNode.style.addLayerAbove(anchor.layerId, tail.layer) | ||
is Anchor.Below -> styleNode.style.addLayerBelow(anchor.layerId, tail.layer) | ||
is Anchor.Replace -> { | ||
val layerToReplace = styleNode.style.getLayer(anchor.layerId)!! | ||
styleNode.style.addLayerAbove(layerToReplace.id, tail.layer) | ||
styleNode.logger?.i { "Replacing layer ${layerToReplace.id} with ${tail.layer.id}" } | ||
styleNode.style.removeLayer(layerToReplace) | ||
replacedLayers[anchor] = layerToReplace | ||
replacementCounters[anchor] = 0 | ||
} | ||
} | ||
tail.markAdded() | ||
|
||
// and add the rest below it | ||
nodes.forEach { node -> | ||
styleNode.logger?.i { "Adding layer ${node.layer.id} below ${tail.layer.id}" } | ||
styleNode.style.addLayerBelow(tail.layer.id, node.layer) | ||
node.markAdded() | ||
} | ||
} | ||
} | ||
|
||
private fun LayerNode<*>.markAdded() { | ||
if (anchor is Anchor.Replace) | ||
replacementCounters[anchor] = replacementCounters.getValue(anchor) + 1 | ||
added = true | ||
} | ||
|
||
private fun Anchor.validate() { | ||
layerIdOrNull?.let { layerId -> | ||
require(baseLayers.containsKey(layerId)) { "Layer ID '$layerId' not found in base style" } | ||
} | ||
} | ||
|
||
private val Anchor.layerIdOrNull: String? | ||
get() = | ||
when (this) { | ||
is Anchor.Above -> layerId | ||
is Anchor.Below -> layerId | ||
is Anchor.Replace -> layerId | ||
else -> null | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/engine/SourceManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package dev.sargunv.maplibrecompose.compose.engine | ||
|
||
import dev.sargunv.maplibrecompose.core.source.Source | ||
|
||
internal class SourceManager(private val node: StyleNode) { | ||
private val baseSources = node.style.getSources().associateBy { it.id } | ||
|
||
private val sourcesToAdd = mutableListOf<Source>() | ||
|
||
private val counter = | ||
ReferenceCounter<Source>( | ||
onZeroToOne = { source -> | ||
node.logger?.i { "Queuing source ${source.id} for addition" } | ||
sourcesToAdd.add(source) | ||
}, | ||
onOneToZero = { source -> | ||
node.logger?.i { "Removing source ${source.id}" } | ||
node.style.removeSource(source) | ||
}, | ||
) | ||
|
||
internal fun getBaseSource(id: String): Source { | ||
return baseSources[id] ?: error("Source ID '$id' not found in base style") | ||
} | ||
|
||
internal fun addReference(source: Source) { | ||
require(source.id !in baseSources) { "Source ID '${source.id}' already exists in base style" } | ||
counter.increment(source) | ||
} | ||
|
||
internal fun removeReference(source: Source) { | ||
require(source.id !in baseSources) { | ||
"Source ID '${source.id}' is part of the base style and can't be removed here" | ||
} | ||
counter.decrement(source) | ||
} | ||
|
||
internal fun applyChanges() { | ||
sourcesToAdd | ||
.onEach { | ||
node.logger?.i { "Adding source ${it.id}" } | ||
node.style.addSource(it) | ||
} | ||
.clear() | ||
} | ||
} |
Oops, something went wrong.