Migrating to 0.11.0
Upgrade to Kotlin 2.0
Doodle now requires Kotlin 2.0. So you'll need to update your app to use the newer version of Kotlin. See installation for instructions.
Adjust to the new layout paradigm
A View
's bounds is no longer editable directly as it was in previous versions of Doodle. This is a major change to the way Doodle layout functions; but it is important to avoid some major pitfalls of the previous approach. Namely, it was very easy to write code that would not produce the expected layout. This is a good example of something that would cause issues before:
import io.nacular.doodle.controls.text.Label
import io.nacular.doodle.core.view
import io.nacular.doodle.layout.constraints.constrain
import io.nacular.doodle.layout.constraints.fill
fun labelFitText() {
//sampleStart
view {
+ Label("Hello") // Label fits its text by default
layout = constrain(children.first(), fill) // ❌ Label would ignore layout
}
//sampleEnd
}
Now it just works as expected since View
s cannot override the Layout
they are managed by.
import io.nacular.doodle.controls.text.Label
import io.nacular.doodle.core.view
import io.nacular.doodle.layout.constraints.constrain
import io.nacular.doodle.layout.constraints.fill
fun labelFitText() {
//sampleStart
view {
+ Label("Hello") // Label fits its text by default
layout = constrain(children.first(), fill) // ✅ works as expected
}
//sampleEnd
}
More specifically, a View
is given a min
and max
size it can take by its parent's Layout
. It then picks a size it would like to take in that range and reports back to the Layout
, which uses that information to position/size it and the other Views it manages.
This means you cannot directly change a View
's bounds
like before; and can only suggest changes (see suggestBounds
etc.) that the View
may use to determine its final size. Therefore, code like the following no longer works, and cannot be directly converted to this new bounds-suggestion system; since bounds suggestions are not guaranteed in the way a normal setter would be.
import io.nacular.doodle.core.view
import io.nacular.doodle.geometry.Size
fun invalidSize() {
//sampleStart
val view1 = view {}.apply { size = Size(10, 50) }
val view2 = view {}.apply { size = view1.size } // view2.size == Size(10, 50)
//sampleEnd
}
import io.nacular.doodle.core.view
fun invalidSize() {
//sampleStart
val view1 = view {}.apply { suggestSize(10.0, 50.0) }
val view2 = view {}.apply { suggestSize(view1.size) } // view2.size == Size.Empty since suggestion usually async
//sampleEnd
}
Update custom Layouts
Your custom Layout
s will need to change because the APIs have evolved in the latest version. Layout
s are no longer aware of the View they are laying out. Instead, they now work with a sequence of Views directly and have all other necessary context provided in the call to layout
.
import io.nacular.doodle.core.Layout
import io.nacular.doodle.core.PositionableContainer
import io.nacular.doodle.geometry.Rectangle
//sampleStart
class SimpleLayout: Layout {
override fun layout(container: PositionableContainer) {
container.children.forEach {
it.bounds = Rectangle(0.0, 0.0, container.width, container.height)
}
}
}
//sampleEnd
import io.nacular.doodle.core.Layout
import io.nacular.doodle.core.Positionable
import io.nacular.doodle.geometry.Rectangle
import io.nacular.doodle.geometry.Size
import io.nacular.doodle.layout.Insets
//sampleStart
class SimpleLayout: Layout {
override fun layout(views: Sequence<Positionable>, min: Size, current: Size, max: Size, insets: Insets): Size {
views.forEach {
it.updateBounds(Rectangle(0.0, 0.0, current.width, current.height))
}
return current
}
}
//sampleEnd