Skip to content

Commit

Permalink
Migrate View Box away from subtype
Browse files Browse the repository at this point in the history
When widgets switch to abstract classes, this will no longer be allowed.
  • Loading branch information
JakeWharton committed Oct 24, 2024
1 parent dd96ce1 commit 7da6444
Show file tree
Hide file tree
Showing 27 changed files with 278 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,18 @@ import app.cash.redwood.layout.modifier.Shrink as ShrinkModifier
import app.cash.redwood.layout.modifier.Size as SizeModifier
import app.cash.redwood.layout.modifier.VerticalAlignment as VerticalAlignmentModifier
import app.cash.redwood.layout.modifier.Width as WidthModifier
import app.cash.redwood.layout.widget.Column
import app.cash.redwood.layout.widget.Row
import app.cash.redwood.ui.Density
import app.cash.redwood.ui.Margin
import app.cash.redwood.yoga.AlignItems
import app.cash.redwood.yoga.AlignSelf
import app.cash.redwood.yoga.JustifyContent
import app.cash.redwood.yoga.Node

internal interface YogaFlexContainer<W : Any> :
Column<W>,
Row<W> {
internal interface YogaFlexContainer<W : Any> {
val rootNode: Node
val density: Density

override fun margin(margin: Margin) {
fun margin(margin: Margin) {
with(rootNode) {
with(density) {
marginStart = margin.start.toPx().toFloat()
Expand All @@ -53,22 +49,6 @@ internal interface YogaFlexContainer<W : Any> :
}
}

override fun horizontalAlignment(horizontalAlignment: MainAxisAlignment) {
mainAxisAlignment(horizontalAlignment)
}

override fun horizontalAlignment(horizontalAlignment: CrossAxisAlignment) {
crossAxisAlignment(horizontalAlignment)
}

override fun verticalAlignment(verticalAlignment: MainAxisAlignment) {
mainAxisAlignment(verticalAlignment)
}

override fun verticalAlignment(verticalAlignment: CrossAxisAlignment) {
crossAxisAlignment(verticalAlignment)
}

fun crossAxisAlignment(crossAxisAlignment: CrossAxisAlignment) {
rootNode.alignItems = crossAxisAlignment.toAlignItems()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import com.example.redwood.testapp.testing.ButtonValue
import com.example.redwood.testapp.widget.Button

class ListeningButton :
Button<WidgetValue>,
Button<WidgetValue>(),
ChangeListener {
private val changes = ArrayList<String>()
fun changes(): List<String> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import com.example.redwood.testapp.testing.TestRowValue
import com.example.redwood.testapp.widget.TestRow

class ListeningTestRow :
TestRow<WidgetValue>,
TestRow<WidgetValue>(),
ChangeListener {
private val changes = ArrayList<String>()
fun changes(): List<String> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import app.cash.redwood.widget.compose.ComposeWidgetChildren

internal class ComposeUiBox(
private val backgroundColor: Int = 0,
) : Box<@Composable () -> Unit> {
) : Box<@Composable () -> Unit>() {
private var modifierTick by mutableIntStateOf(0)
override val children = ComposeWidgetChildren(onModifierUpdated = { modifierTick++ })

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import app.cash.redwood.layout.api.Constraint
import app.cash.redwood.layout.api.CrossAxisAlignment
import app.cash.redwood.layout.api.MainAxisAlignment
import app.cash.redwood.layout.api.Overflow
import app.cash.redwood.layout.widget.Column
import app.cash.redwood.layout.widget.Row
import app.cash.redwood.ui.Density
import app.cash.redwood.ui.Margin
import app.cash.redwood.ui.Px
Expand All @@ -56,14 +58,46 @@ import app.cash.redwood.yoga.Node
import app.cash.redwood.yoga.Size
import app.cash.redwood.yoga.isHorizontal

internal class ComposeUiColumn : Column<@Composable () -> Unit>() {
private val container = ComposeUiFlexContainer(FlexDirection.Row)

override val value get() = container.value
override var modifier by container::modifier
override val children get() = container.children

override fun width(width: Constraint) = container.width(width)
override fun height(height: Constraint) = container.height(height)
override fun margin(margin: Margin) = container.margin(margin)
override fun overflow(overflow: Overflow) = container.overflow(overflow)
override fun horizontalAlignment(horizontalAlignment: CrossAxisAlignment) = container.crossAxisAlignment(horizontalAlignment)
override fun verticalAlignment(verticalAlignment: MainAxisAlignment) = container.mainAxisAlignment(verticalAlignment)
override fun onScroll(onScroll: ((Px) -> Unit)?) = container.onScroll(onScroll)
}

internal class ComposeUiRow : Row<@Composable () -> Unit>() {
private val container = ComposeUiFlexContainer(FlexDirection.Row)

override val value get() = container.value
override var modifier by container::modifier
override val children get() = container.children

override fun width(width: Constraint) = container.width(width)
override fun height(height: Constraint) = container.height(height)
override fun margin(margin: Margin) = container.margin(margin)
override fun overflow(overflow: Overflow) = container.overflow(overflow)
override fun horizontalAlignment(horizontalAlignment: MainAxisAlignment) = container.mainAxisAlignment(horizontalAlignment)
override fun verticalAlignment(verticalAlignment: CrossAxisAlignment) = container.crossAxisAlignment(verticalAlignment)
override fun onScroll(onScroll: ((Px) -> Unit)?) = container.onScroll(onScroll)
}

internal class ComposeUiFlexContainer(
private val flexDirection: FlexDirection,
) : YogaFlexContainer<@Composable () -> Unit> {
override val rootNode = Node().apply {
flexDirection = this@ComposeUiFlexContainer.flexDirection
}
override val children = ComposeWidgetChildren()
override var modifier: RedwoodModifier = RedwoodModifier
val children = ComposeWidgetChildren()
var modifier: RedwoodModifier = RedwoodModifier

private var recomposeTick by mutableIntStateOf(0)
private var width by mutableStateOf(Constraint.Wrap)
Expand All @@ -76,23 +110,23 @@ internal class ComposeUiFlexContainer(
internal var testOnlyModifier: Modifier? = null
internal var scrollState: ScrollState? = null

override fun width(width: Constraint) {
fun width(width: Constraint) {
this.width = width
}

override fun height(height: Constraint) {
fun height(height: Constraint) {
this.height = height
}

override fun margin(margin: Margin) {
this.margin = margin
}

override fun overflow(overflow: Overflow) {
fun overflow(overflow: Overflow) {
this.overflow = overflow
}

override fun onScroll(onScroll: ((Px) -> Unit)?) {
fun onScroll(onScroll: ((Px) -> Unit)?) {
this.onScroll = onScroll
}

Expand All @@ -110,7 +144,7 @@ internal class ComposeUiFlexContainer(
recomposeTick++
}

override val value: @Composable () -> Unit = @Composable {
val value: @Composable () -> Unit = @Composable {
Layout(
content = {
// Observe this so we can manually trigger recomposition.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ import app.cash.redwood.layout.widget.Column
import app.cash.redwood.layout.widget.RedwoodLayoutWidgetFactory
import app.cash.redwood.layout.widget.Row
import app.cash.redwood.layout.widget.Spacer
import app.cash.redwood.yoga.FlexDirection

public class ComposeUiRedwoodLayoutWidgetFactory : RedwoodLayoutWidgetFactory<@Composable () -> Unit> {
override fun Box(): Box<@Composable () -> Unit> = ComposeUiBox()
override fun Column(): Column<@Composable () -> Unit> = ComposeUiFlexContainer(FlexDirection.Column)
override fun Row(): Row<@Composable () -> Unit> = ComposeUiFlexContainer(FlexDirection.Row)
override fun Column(): Column<@Composable () -> Unit> = ComposeUiColumn()
override fun Row(): Row<@Composable () -> Unit> = ComposeUiRow()
override fun Spacer(): Spacer<@Composable () -> Unit> = ComposeUiSpacer()
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import app.cash.redwood.layout.widget.Spacer
import app.cash.redwood.ui.Dp
import app.cash.redwood.ui.dp

internal class ComposeUiSpacer : Spacer<@Composable () -> Unit> {
internal class ComposeUiSpacer : Spacer<@Composable () -> Unit>() {
private var width by mutableStateOf(0.dp)
private var height by mutableStateOf(0.dp)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,60 +40,85 @@ import app.cash.redwood.widget.Widget
import org.w3c.dom.Document
import org.w3c.dom.HTMLDivElement
import org.w3c.dom.HTMLElement
import org.w3c.dom.css.CSSStyleDeclaration
import org.w3c.dom.events.Event
import org.w3c.dom.events.EventListener

public class HTMLElementRedwoodLayoutWidgetFactory(
private val document: Document,
) : RedwoodLayoutWidgetFactory<HTMLElement> {
override fun Box(): Box<HTMLElement> {
override fun Box(): Box<HTMLElement> =
TODO("Not yet implemented")
}

override fun Column(): Column<HTMLElement> =
HTMLFlexContainer(
value = document.createElement("div") as HTMLDivElement,
direction = "column",
overflowSetter = { style.overflowY = it },
)
HTMLColumn(document.createElement("div") as HTMLDivElement)

override fun Row(): Row<HTMLElement> =
HTMLFlexContainer(
value = document.createElement("div") as HTMLDivElement,
direction = "row",
overflowSetter = { style.overflowX = it },
)
HTMLRow(document.createElement("div") as HTMLDivElement)

override fun Spacer(): Spacer<HTMLElement> =
HTMLSpacer(
value = document.createElement("div") as HTMLDivElement,
)
HTMLSpacer(document.createElement("div") as HTMLDivElement)
}

private class HTMLColumn(
value: HTMLDivElement,
) : Column<HTMLElement>() {
private val container = HTMLFlexContainer(value, "column") { overflowY = it }

override val value get() = container.value
override var modifier by container::modifier
override val children get() = container.children

override fun width(width: Constraint) = container.width(width)
override fun height(height: Constraint) = container.height(height)
override fun margin(margin: Margin) = container.margin(margin)
override fun overflow(overflow: Overflow) = container.overflow(overflow)
override fun horizontalAlignment(horizontalAlignment: CrossAxisAlignment) = container.crossAxisAlignment(horizontalAlignment)
override fun verticalAlignment(verticalAlignment: MainAxisAlignment) = container.mainAxisAlignment(verticalAlignment)
override fun onScroll(onScroll: ((Px) -> Unit)?) = container.onScroll(onScroll)
}

private class HTMLRow(
value: HTMLDivElement,
) : Row<HTMLElement>() {
private val container = HTMLFlexContainer(value, "row") { overflowX = it }

override val value get() = container.value
override var modifier by container::modifier
override val children get() = container.children

override fun width(width: Constraint) = container.width(width)
override fun height(height: Constraint) = container.height(height)
override fun margin(margin: Margin) = container.margin(margin)
override fun overflow(overflow: Overflow) = container.overflow(overflow)
override fun horizontalAlignment(horizontalAlignment: MainAxisAlignment) = container.mainAxisAlignment(horizontalAlignment)
override fun verticalAlignment(verticalAlignment: CrossAxisAlignment) = container.crossAxisAlignment(verticalAlignment)
override fun onScroll(onScroll: ((Px) -> Unit)?) = container.onScroll(onScroll)
}

private class HTMLFlexContainer(
override val value: HTMLDivElement,
val value: HTMLDivElement,
direction: String,
private val overflowSetter: HTMLDivElement.(String) -> Unit,
) : Column<HTMLElement>,
Row<HTMLElement> {
private val overflowSetter: CSSStyleDeclaration.(String) -> Unit,
) {
init {
value.style.display = "flex"
value.style.flexDirection = direction
}

override val children: Widget.Children<HTMLElement> = HTMLFlexElementChildren(value)
val children: Widget.Children<HTMLElement> = HTMLFlexElementChildren(value)

private var scrollEventListener: EventListener? = null

override fun width(width: Constraint) {
fun width(width: Constraint) {
value.style.width = width.toCss()
}

override fun height(height: Constraint) {
fun height(height: Constraint) {
value.style.height = height.toCss()
}

override fun margin(margin: Margin) {
fun margin(margin: Margin) {
value.style.apply {
marginInlineStart = margin.start.toPxString()
marginInlineEnd = margin.end.toPxString()
Expand All @@ -102,11 +127,11 @@ private class HTMLFlexContainer(
}
}

override fun overflow(overflow: Overflow) {
value.overflowSetter(overflow.toCss())
fun overflow(overflow: Overflow) {
value.style.overflowSetter(overflow.toCss())
}

override fun onScroll(onScroll: ((Px) -> Unit)?) {
fun onScroll(onScroll: ((Px) -> Unit)?) {
scrollEventListener?.let { eventListener ->
value.removeEventListener("scroll", eventListener)
scrollEventListener = null
Expand All @@ -127,31 +152,15 @@ private class HTMLFlexContainer(
}
}

override fun horizontalAlignment(horizontalAlignment: MainAxisAlignment) {
mainAxisAlignment(horizontalAlignment)
}

override fun horizontalAlignment(horizontalAlignment: CrossAxisAlignment) {
crossAxisAlignment(horizontalAlignment)
}

override fun verticalAlignment(verticalAlignment: MainAxisAlignment) {
mainAxisAlignment(verticalAlignment)
}

override fun verticalAlignment(verticalAlignment: CrossAxisAlignment) {
crossAxisAlignment(verticalAlignment)
}

private fun crossAxisAlignment(crossAxisAlignment: CrossAxisAlignment) {
fun crossAxisAlignment(crossAxisAlignment: CrossAxisAlignment) {
value.style.alignItems = crossAxisAlignment.toCss()
}

private fun mainAxisAlignment(mainAxisAlignment: MainAxisAlignment) {
fun mainAxisAlignment(mainAxisAlignment: MainAxisAlignment) {
value.style.justifyContent = mainAxisAlignment.toCss()
}

override var modifier: Modifier = Modifier
var modifier: Modifier = Modifier
}

private class HTMLSpacer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import platform.UIKit.UIView
import platform.darwin.NSInteger

internal class UIViewBox :
Box<UIView>,
Box<UIView>(),
ResizableWidget<UIView> {
override val value: View = View()

Expand Down
Loading

0 comments on commit 7da6444

Please sign in to comment.