Where's my view?
The following app has a single top-level view that fills the Display and draws a centered circle.
package simplecircle
import io.nacular.doodle.application.Application
import io.nacular.doodle.application.application
import io.nacular.doodle.core.Display
import io.nacular.doodle.core.center
import io.nacular.doodle.core.height
import io.nacular.doodle.core.view
import io.nacular.doodle.core.width
import io.nacular.doodle.drawing.Color.Companion.Red
import io.nacular.doodle.drawing.Color.Companion.White
import io.nacular.doodle.drawing.paint
import io.nacular.doodle.geometry.Circle
import org.kodein.di.instance
import kotlin.math.min
//sampleStart
class SimpleCircle(display: Display): Application {
init {
display += view {
size = display.size
render = {
circle(
Circle(
center = display.center,
radius = min(display.width, display.height) / 2 - 10
), fill = Red.paint)
}
}
display.fill(White.paint)
}
override fun shutdown() {}
}
fun main() {
application {
SimpleCircle(display = instance())
}
}
//sampleEnd
Where is my Button?
Now we try the same thing with a PushButton
. The following code feels like it should work; but it doesn't.
package invisiblebutton
import io.nacular.doodle.application.Application
import io.nacular.doodle.application.application
import io.nacular.doodle.controls.buttons.PushButton
import io.nacular.doodle.core.Display
import io.nacular.doodle.drawing.Color.Companion.White
import io.nacular.doodle.drawing.paint
import io.nacular.doodle.geometry.Size
import org.kodein.di.instance
//sampleStart
class InvisibleButton(display: Display): Application {
init {
// NOTE: This does not render because the button has no
// Behavior installed
display += PushButton().apply { size = Size(40, 20) }
display.fill(White.paint)
}
override fun shutdown() {}
}
fun main() {
application {
InvisibleButton(display = instance())
}
}
//sampleEnd
The above examples does not render anything because PushButton
delegates all rendering to its behavior
and the button has none specified.
Setting a Behavior fixes the button
The above example does not render anything because the button has no Behavior
installed. PushButton
(like many Views in the controls
library) does not render directly itself, but relies on its behavior
for all drawing.
So we can fix the above app by explicitly adding a Behavior
to the button.
package buttonwithbehavior
import io.nacular.doodle.application.Application
import io.nacular.doodle.application.Modules.Companion.PointerModule
import io.nacular.doodle.application.application
import io.nacular.doodle.controls.buttons.PushButton
import io.nacular.doodle.controls.theme.simpleButtonRenderer
import io.nacular.doodle.core.Display
import io.nacular.doodle.drawing.Color.Companion.Lightgray
import io.nacular.doodle.drawing.Color.Companion.White
import io.nacular.doodle.drawing.darker
import io.nacular.doodle.drawing.paint
import io.nacular.doodle.geometry.Size
import io.nacular.doodle.layout.constraints.center
import io.nacular.doodle.layout.constraints.constrain
import org.kodein.di.instance
//sampleStart
class ButtonWithBehavior(display: Display): Application {
init {
display += PushButton("Hi").apply {
size = Size(80, 40)
// Assign a Behavior to the button to handle rendering
behavior = simpleButtonRenderer { button, canvas ->
var color = Lightgray
if (button.model.pressed ) color = color.darker(0.25f)
if (button.model.pointerOver) color = color.darker(0.25f)
canvas.rect(button.bounds.atOrigin, radius = 10.0, fill = color.paint)
}
// acceptsThemes = false // prevents any theme from overriding behavior
}
display.layout = constrain(display.first(), center)
display.fill(White.paint)
}
override fun shutdown() {}
}
fun main() {
// PointerModule required to support mouse/touch
application(modules = listOf(PointerModule)) {
ButtonWithBehavior(display = instance())
}
}
//sampleEnd
A Theme could also be used
Themes are a great way to assign behaviors to your entire app all at once. In this version we register the nativeButtonBehavior
(Web, Desktop) module which makes a Theme
available that will assign a native button behavior to all buttons in the app by default. This theme is injected into the app along with the ThemeManager
which is used to select the theme. The result is that our button now has a behavior and delegates all rendering to it.
package buttonwiththeme
import io.nacular.doodle.application.Application
import io.nacular.doodle.application.Modules.Companion.PointerModule
import io.nacular.doodle.application.application
import io.nacular.doodle.controls.buttons.PushButton
import io.nacular.doodle.core.Display
import io.nacular.doodle.docs.apps.ButtonWithTheme
import io.nacular.doodle.drawing.Color.Companion.White
import io.nacular.doodle.drawing.paint
import io.nacular.doodle.geometry.Size
import io.nacular.doodle.layout.constraints.center
import io.nacular.doodle.layout.constraints.constrain
import io.nacular.doodle.theme.Theme
import io.nacular.doodle.theme.ThemeManager
import io.nacular.doodle.theme.native.NativeTheme.Companion.nativeButtonBehavior
import org.kodein.di.instance
//sampleStart
class ButtonWithTheme(display: Display, themeManager: ThemeManager, theme: Theme): Application {
init {
// install theme that provides a button behavior
themeManager.selected = theme
display += PushButton("Hi").apply { size = Size(80, 40) }
display.layout = constrain(display.first(), center)
display.fill(White.paint)
}
override fun shutdown() {}
}
fun main() {
application(modules = listOf(PointerModule, nativeButtonBehavior())) {
ButtonWithTheme(
display = instance(),
themeManager = instance(),
theme = instance()
)
}
}
//sampleEnd