Skip to main content

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
danger

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 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