mirror of
				https://github.com/JakeWharton/mosaic.git
				synced 2025-10-31 10:48:43 +08:00 
			
		
		
		
	Shuffle things around for cleaner layering (#164)
The 'layout' package is now a standalone widget tree. The 'ui' package is now only composables. The root package contains all the code for integration and Compose.
This commit is contained in:
		| @ -1,5 +0,0 @@ | ||||
| package com.jakewharton.mosaic.layout | ||||
|  | ||||
| public fun interface NoContentMeasurePolicy { | ||||
| 	public fun NoContentMeasureScope.measure(): MeasureResult | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| package com.jakewharton.mosaic.layout | ||||
|  | ||||
| public sealed class NoContentMeasureScope { | ||||
| 	public fun layout( | ||||
| 		width: Int, | ||||
| 		height: Int, | ||||
| 	): MeasureResult { | ||||
| 		return LayoutResult(width, height) | ||||
| 	} | ||||
|  | ||||
| 	private class LayoutResult( | ||||
| 		override val width: Int, | ||||
| 		override val height: Int, | ||||
| 	) : MeasureResult { | ||||
| 		override fun placeChildren() {} | ||||
| 	} | ||||
|  | ||||
| 	internal companion object : NoContentMeasureScope() | ||||
| } | ||||
| @ -1,14 +1,7 @@ | ||||
| package com.jakewharton.mosaic | ||||
| package com.jakewharton.mosaic.layout | ||||
| 
 | ||||
| import androidx.compose.runtime.AbstractApplier | ||||
| import androidx.compose.runtime.Applier | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.ReusableComposeNode | ||||
| import com.jakewharton.mosaic.layout.Measurable | ||||
| import com.jakewharton.mosaic.layout.MeasurePolicy | ||||
| import com.jakewharton.mosaic.layout.MeasureResult | ||||
| import com.jakewharton.mosaic.layout.MeasureScope | ||||
| import com.jakewharton.mosaic.layout.Placeable | ||||
| import com.jakewharton.mosaic.TextCanvas | ||||
| import com.jakewharton.mosaic.TextSurface | ||||
| import com.jakewharton.mosaic.layout.Placeable.PlacementScope | ||||
| 
 | ||||
| internal fun interface DrawPolicy { | ||||
| @ -148,87 +141,4 @@ internal class MosaicNode( | ||||
| 	fun paintStatics(statics: MutableList<TextSurface>) = staticPaintPolicy?.run { performPaintStatics(statics) } | ||||
| 
 | ||||
| 	override fun toString() = debugPolicy.run { renderDebug() } | ||||
| 
 | ||||
| 	companion object { | ||||
| 		val Factory: () -> MosaicNode = { | ||||
| 			MosaicNode( | ||||
| 				measurePolicy = ThrowingPolicy, | ||||
| 				drawPolicy = ThrowingPolicy, | ||||
| 				staticPaintPolicy = ThrowingPolicy, | ||||
| 				debugPolicy = ThrowingPolicy, | ||||
| 			) | ||||
| 		} | ||||
| 
 | ||||
| 		fun root(): MosaicNode { | ||||
| 			return MosaicNode( | ||||
| 				measurePolicy = { measurables -> | ||||
| 					var width = 0 | ||||
| 					var height = 0 | ||||
| 					val placeables = measurables.map { measurable -> | ||||
| 						measurable.measure().also { | ||||
| 							width = maxOf(width, it.width) | ||||
| 							height = maxOf(height, it.height) | ||||
| 						} | ||||
| 					} | ||||
| 					layout(width, height) { | ||||
| 						for (placeable in placeables) { | ||||
| 							placeable.place(0, 0) | ||||
| 						} | ||||
| 					} | ||||
| 				}, | ||||
| 				drawPolicy = null, | ||||
| 				staticPaintPolicy = StaticPaintPolicy.Children, | ||||
| 				debugPolicy = { | ||||
| 					children.joinToString(separator = "\n") | ||||
| 				} | ||||
| 			) | ||||
| 		} | ||||
| 
 | ||||
| 		private val ThrowingPolicy = object : MeasurePolicy, DrawPolicy, StaticPaintPolicy, DebugPolicy { | ||||
| 			override fun MeasureScope.measure(measurables: List<Measurable>) = throw AssertionError() | ||||
| 			override fun performDraw(canvas: TextCanvas) = throw AssertionError() | ||||
| 			override fun MosaicNode.performPaintStatics(statics: MutableList<TextSurface>) = throw AssertionError() | ||||
| 			override fun MosaicNode.renderDebug() = throw AssertionError() | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| internal inline fun Node( | ||||
| 	content: @Composable () -> Unit = {}, | ||||
| 	measurePolicy: MeasurePolicy, | ||||
| 	drawPolicy: DrawPolicy?, | ||||
| 	staticPaintPolicy: StaticPaintPolicy?, | ||||
| 	debugPolicy: DebugPolicy, | ||||
| ) { | ||||
| 	ReusableComposeNode<MosaicNode, Applier<Any>>( | ||||
| 		factory = MosaicNode.Factory, | ||||
| 		update = { | ||||
| 			set(measurePolicy) { this.measurePolicy = measurePolicy } | ||||
| 			set(drawPolicy) { this.drawPolicy = drawPolicy } | ||||
| 			set(staticPaintPolicy) { this.staticPaintPolicy = staticPaintPolicy } | ||||
| 			set(debugPolicy) { this.debugPolicy = debugPolicy } | ||||
| 		}, | ||||
| 		content = content, | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| internal class MosaicNodeApplier(root: MosaicNode) : AbstractApplier<MosaicNode>(root) { | ||||
| 	override fun insertTopDown(index: Int, instance: MosaicNode) { | ||||
| 		// Ignored, we insert bottom-up. | ||||
| 	} | ||||
| 
 | ||||
| 	override fun insertBottomUp(index: Int, instance: MosaicNode) { | ||||
| 		current.children.add(index, instance) | ||||
| 	} | ||||
| 
 | ||||
| 	override fun remove(index: Int, count: Int) { | ||||
| 		current.children.remove(index, count) | ||||
| 	} | ||||
| 
 | ||||
| 	override fun move(from: Int, to: Int, count: Int) { | ||||
| 		current.children.move(from, to, count) | ||||
| 	} | ||||
| 
 | ||||
| 	override fun onClear() {} | ||||
| } | ||||
| @ -1,10 +1,13 @@ | ||||
| package com.jakewharton.mosaic | ||||
|  | ||||
| import androidx.compose.runtime.AbstractApplier | ||||
| import androidx.compose.runtime.BroadcastFrameClock | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.Composition | ||||
| import androidx.compose.runtime.Recomposer | ||||
| import androidx.compose.runtime.snapshots.Snapshot | ||||
| import com.jakewharton.mosaic.layout.MosaicNode | ||||
| import com.jakewharton.mosaic.layout.StaticPaintPolicy | ||||
| import kotlin.time.ExperimentalTime | ||||
| import kotlinx.coroutines.CompletableDeferred | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| @ -26,7 +29,7 @@ internal fun mosaicNodes(content: @Composable () -> Unit): MosaicNode { | ||||
| 	val job = Job() | ||||
| 	val composeContext = clock + job | ||||
|  | ||||
| 	val rootNode = MosaicNode.root() | ||||
| 	val rootNode = createRootNode() | ||||
| 	val recomposer = Recomposer(composeContext) | ||||
| 	val composition = Composition(MosaicNodeApplier(rootNode), recomposer) | ||||
|  | ||||
| @ -64,7 +67,7 @@ public suspend fun runMosaic(body: suspend MosaicScope.() -> Unit): Unit = corou | ||||
| 	val job = Job(coroutineContext[Job]) | ||||
| 	val composeContext = coroutineContext + clock + job | ||||
|  | ||||
| 	val rootNode = MosaicNode.root() | ||||
| 	val rootNode = createRootNode() | ||||
| 	val recomposer = Recomposer(composeContext) | ||||
| 	val composition = Composition(MosaicNodeApplier(rootNode), recomposer) | ||||
|  | ||||
| @ -136,3 +139,48 @@ public suspend fun runMosaic(body: suspend MosaicScope.() -> Unit): Unit = corou | ||||
| 	job.cancel() | ||||
| 	composition.dispose() | ||||
| } | ||||
|  | ||||
| internal fun createRootNode(): MosaicNode { | ||||
| 	return MosaicNode( | ||||
| 		measurePolicy = { measurables -> | ||||
| 			var width = 0 | ||||
| 			var height = 0 | ||||
| 			val placeables = measurables.map { measurable -> | ||||
| 				measurable.measure().also { | ||||
| 					width = maxOf(width, it.width) | ||||
| 					height = maxOf(height, it.height) | ||||
| 				} | ||||
| 			} | ||||
| 			layout(width, height) { | ||||
| 				for (placeable in placeables) { | ||||
| 					placeable.place(0, 0) | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 		drawPolicy = null, | ||||
| 		staticPaintPolicy = StaticPaintPolicy.Children, | ||||
| 		debugPolicy = { | ||||
| 			children.joinToString(separator = "\n") | ||||
| 		} | ||||
| 	) | ||||
| } | ||||
|  | ||||
| internal class MosaicNodeApplier(root: MosaicNode) : AbstractApplier<MosaicNode>(root) { | ||||
| 	override fun insertTopDown(index: Int, instance: MosaicNode) { | ||||
| 		// Ignored, we insert bottom-up. | ||||
| 	} | ||||
|  | ||||
| 	override fun insertBottomUp(index: Int, instance: MosaicNode) { | ||||
| 		current.children.add(index, instance) | ||||
| 	} | ||||
|  | ||||
| 	override fun remove(index: Int, count: Int) { | ||||
| 		current.children.remove(index, count) | ||||
| 	} | ||||
|  | ||||
| 	override fun move(from: Int, to: Int, count: Int) { | ||||
| 		current.children.move(from, to, count) | ||||
| 	} | ||||
|  | ||||
| 	override fun onClear() {} | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| package com.jakewharton.mosaic | ||||
|  | ||||
| import com.jakewharton.mosaic.layout.MosaicNode | ||||
| import kotlin.time.ExperimentalTime | ||||
| import kotlin.time.TimeMark | ||||
| import kotlin.time.TimeSource | ||||
|  | ||||
| @ -3,7 +3,6 @@ | ||||
| package com.jakewharton.mosaic.ui | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import com.jakewharton.mosaic.layout.Layout | ||||
| import kotlin.jvm.JvmName | ||||
|  | ||||
| @Composable | ||||
|  | ||||
| @ -1,13 +1,38 @@ | ||||
| @file:JvmName("Layout") | ||||
| 
 | ||||
| package com.jakewharton.mosaic.layout | ||||
| package com.jakewharton.mosaic.ui | ||||
| 
 | ||||
| import androidx.compose.runtime.Composable | ||||
| import com.jakewharton.mosaic.DrawPolicy | ||||
| import com.jakewharton.mosaic.Node | ||||
| import com.jakewharton.mosaic.StaticPaintPolicy | ||||
| import com.jakewharton.mosaic.layout.DrawPolicy | ||||
| import com.jakewharton.mosaic.layout.Measurable | ||||
| import com.jakewharton.mosaic.layout.MeasurePolicy | ||||
| import com.jakewharton.mosaic.layout.MeasureResult | ||||
| import com.jakewharton.mosaic.layout.MeasureScope | ||||
| import com.jakewharton.mosaic.layout.StaticPaintPolicy | ||||
| import kotlin.jvm.JvmName | ||||
| 
 | ||||
| internal fun interface NoContentMeasurePolicy { | ||||
| 	fun NoContentMeasureScope.measure(): MeasureResult | ||||
| } | ||||
| 
 | ||||
| internal sealed class NoContentMeasureScope { | ||||
| 	fun layout( | ||||
| 		width: Int, | ||||
| 		height: Int, | ||||
| 	): MeasureResult { | ||||
| 		return LayoutResult(width, height) | ||||
| 	} | ||||
| 
 | ||||
| 	private class LayoutResult( | ||||
| 		override val width: Int, | ||||
| 		override val height: Int, | ||||
| 	) : MeasureResult { | ||||
| 		override fun placeChildren() {} | ||||
| 	} | ||||
| 
 | ||||
| 	internal companion object : NoContentMeasureScope() | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| internal fun Layout( | ||||
| 	debugInfo: () -> String = { "Layout()" }, | ||||
| @ -0,0 +1,50 @@ | ||||
| package com.jakewharton.mosaic.ui | ||||
|  | ||||
| import androidx.compose.runtime.Applier | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.ReusableComposeNode | ||||
| import com.jakewharton.mosaic.TextCanvas | ||||
| import com.jakewharton.mosaic.TextSurface | ||||
| import com.jakewharton.mosaic.layout.DebugPolicy | ||||
| import com.jakewharton.mosaic.layout.DrawPolicy | ||||
| import com.jakewharton.mosaic.layout.Measurable | ||||
| import com.jakewharton.mosaic.layout.MeasurePolicy | ||||
| import com.jakewharton.mosaic.layout.MeasureScope | ||||
| import com.jakewharton.mosaic.layout.MosaicNode | ||||
| import com.jakewharton.mosaic.layout.StaticPaintPolicy | ||||
|  | ||||
| @Composable | ||||
| internal inline fun Node( | ||||
| 	content: @Composable () -> Unit = {}, | ||||
| 	measurePolicy: MeasurePolicy, | ||||
| 	drawPolicy: DrawPolicy?, | ||||
| 	staticPaintPolicy: StaticPaintPolicy?, | ||||
| 	debugPolicy: DebugPolicy, | ||||
| ) { | ||||
| 	ReusableComposeNode<MosaicNode, Applier<Any>>( | ||||
| 		factory = NodeFactory, | ||||
| 		update = { | ||||
| 			set(measurePolicy) { this.measurePolicy = measurePolicy } | ||||
| 			set(drawPolicy) { this.drawPolicy = drawPolicy } | ||||
| 			set(staticPaintPolicy) { this.staticPaintPolicy = staticPaintPolicy } | ||||
| 			set(debugPolicy) { this.debugPolicy = debugPolicy } | ||||
| 		}, | ||||
| 		content = content, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| internal val NodeFactory: () -> MosaicNode = { | ||||
| 	MosaicNode( | ||||
| 		measurePolicy = ThrowingPolicy, | ||||
| 		drawPolicy = ThrowingPolicy, | ||||
| 		staticPaintPolicy = ThrowingPolicy, | ||||
| 		debugPolicy = ThrowingPolicy, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| private val ThrowingPolicy = object : MeasurePolicy, DrawPolicy, StaticPaintPolicy, DebugPolicy { | ||||
| 	override fun MeasureScope.measure(measurables: List<Measurable>) = throw AssertionError() | ||||
| 	override fun performDraw(canvas: TextCanvas) = throw AssertionError() | ||||
| 	override fun MosaicNode.performPaintStatics(statics: MutableList<TextSurface>) = throw AssertionError() | ||||
| 	override fun MosaicNode.renderDebug() = throw AssertionError() | ||||
| } | ||||
| @ -3,7 +3,6 @@ | ||||
| package com.jakewharton.mosaic.ui | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import com.jakewharton.mosaic.layout.Layout | ||||
| import kotlin.jvm.JvmName | ||||
|  | ||||
| @Composable | ||||
|  | ||||
| @ -8,7 +8,6 @@ import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.runtime.snapshots.SnapshotStateList | ||||
| import com.jakewharton.mosaic.Node | ||||
| import kotlin.jvm.JvmName | ||||
|  | ||||
| /** | ||||
|  | ||||
| @ -4,7 +4,6 @@ package com.jakewharton.mosaic.ui | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.remember | ||||
| import com.jakewharton.mosaic.layout.Layout | ||||
| import com.jakewharton.mosaic.text.TextLayout | ||||
| import kotlin.jvm.JvmName | ||||
|  | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| package com.jakewharton.mosaic | ||||
|  | ||||
| import com.jakewharton.mosaic.layout.Layout | ||||
| import com.jakewharton.mosaic.ui.Layout | ||||
| import com.jakewharton.mosaic.ui.Row | ||||
| import com.jakewharton.mosaic.ui.Static | ||||
| import com.jakewharton.mosaic.ui.Text | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| package com.jakewharton.mosaic | ||||
|  | ||||
| import com.jakewharton.mosaic.layout.Layout | ||||
| import com.jakewharton.mosaic.layout.Measurable | ||||
| import com.jakewharton.mosaic.ui.Column | ||||
| import com.jakewharton.mosaic.ui.Layout | ||||
| import com.jakewharton.mosaic.ui.Row | ||||
| import com.jakewharton.mosaic.ui.Text | ||||
| import kotlin.test.Test | ||||
|  | ||||
| @ -1,11 +1,14 @@ | ||||
| package com.jakewharton.mosaic | ||||
|  | ||||
| import androidx.compose.runtime.Applier | ||||
| import com.jakewharton.mosaic.layout.DebugPolicy | ||||
| import com.jakewharton.mosaic.layout.MosaicNode | ||||
| import com.jakewharton.mosaic.ui.NodeFactory | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertEquals | ||||
|  | ||||
| class NodeApplierTest { | ||||
| 	private val root = MosaicNode.root() | ||||
| 	private val root = createRootNode() | ||||
| 	private val applier = MosaicNodeApplier(root) | ||||
|  | ||||
| 	private fun <T> Applier<T>.insert(index: Int, instance: T) { | ||||
| @ -185,7 +188,7 @@ class NodeApplierTest { | ||||
| 	} | ||||
|  | ||||
| 	private fun TextNode(name: String): MosaicNode { | ||||
| 		return MosaicNode.Factory().apply { | ||||
| 		return NodeFactory().apply { | ||||
| 			debugPolicy = DebugPolicy { name } | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jake Wharton
					Jake Wharton