Skip to main content

Displays

Back on the Applications page, HelloApp took a Display as a constructor parameter and put it straight to work showing some text:

package moreusefulapp import io.nacular.doodle.application.Application import io.nacular.doodle.core.Display import io.nacular.doodle.core.view import io.nacular.doodle.drawing.Color.Companion.Black import io.nacular.doodle.drawing.text //sampleStart class HelloApp(display: Display): Application { init { display += view { render = { text("Hello, world!", color = Black) } } } override fun shutdown() {} } //sampleEnd

Let's find out what that display object actually is, and what it's capable of.

What is a Display?​

The Display is your app's root container — it represents the window (Desktop) or page (Web) your app is drawing into, and it holds every View your app shows, directly or indirectly.

Here's the thing that trips almost everyone up the first time: the Display is not itself a View. It plays the part convincingly — it holds children, and you can apply a Layout to it — but it's missing a few things a real View has. In practice, that means:

  • The Display has no parent. It's the top of the hierarchy — there's nothing above it.
  • Views added directly to the Display are called top-level Views, and they have no parent either (more on this below).
tip

Just like a View, the Display can have a Layout applied to it to position its top-level Views.

Getting a Display​

Good news: you've already done this. Back on Applications, we added Display as a constructor parameter and supplied it with instance() at launch time.

package moreusefulapp import io.nacular.doodle.application.application import org.kodein.di.instance //sampleStart fun main() { application { HelloApp(display = instance()) } } //sampleEnd

A Display is available for injection by default — you don't need any extra modules to get one.

Adding and removing top-level Views​

Your app isn't limited to one top-level View — add and remove as many as you like with += and -= on the Display:

package display import io.nacular.doodle.application.Application import io.nacular.doodle.core.Display import io.nacular.doodle.core.view //sampleStart class SampleApp(display: Display): Application { init { val header = view {} // these are empty for now val sidebar = view {} // see Views and Rendering for how to draw into them // add top-level views display += header display += sidebar // remove a top-level view later, e.g. in response to something the user does display -= sidebar } override fun shutdown() { /*...*/ } } //sampleEnd

"Top-level" is just a name for "added straight to the Display, rather than tucked inside another View." That's also why top-level Views have no parent — the Display sits above them, sure, but the Display isn't a View, so there's nothing to point to.

caution

A View's parent property doesn't tell you whether it's actually on screen. Use displayed for that instead.

Where does the Display actually live?​

This part depends on which platform you're aiming at, and you don't need it to get moving — feel free to skip ahead to Views and circle back later.

On the Web: the whole page, or an element​

By default, a top-level Web app's Display claims the entire page — it's tied to the page's <body>. Launch your app inside a specific HTML element however (see hosting in HTML elements), and the Display ties itself to that element instead.

package display import io.nacular.doodle.application.application import org.kodein.di.DI import org.kodein.di.instance import org.w3c.dom.HTMLElement fun topLevelLaunch(element: HTMLElement, moduleA: DI.Module, moduleB: DI.Module) { //sampleStart // Launch top-level app (Display fills the page body) application(modules = listOf(moduleA, moduleB)) { MyApp(display = instance()) } // Launch app within a specific element (Display fills that element) application(element, modules = listOf(moduleA, moduleB)) { MyApp(display = instance()) } //sampleEnd }

Your app genuinely can't tell the difference from the inside — either way, it just has a Display to draw into. The same holds for nested apps: their Display fills whatever View is hosting them, and resizes right along with it.

On Desktop: one Display per window​

Single-window Desktop apps are no different from Web apps — inject Display, and you're off. But hand your app multiple windows to manage, and each one gets its own Display:

package display import io.nacular.doodle.application.Application import io.nacular.doodle.application.application import io.nacular.doodle.core.WindowGroup import io.nacular.doodle.core.view import org.kodein.di.instance //sampleStart class MultiWindowApp(windows: WindowGroup): Application { init { // the main window's Display windows.main.display += view {} // a new window, with its own separate Display windows { title = "New Window" display += view {} } } override fun shutdown() {} } fun main() { application { MultiWindowApp(windows = instance()) } } //sampleEnd

Each Display still plays by all the same rules laid out above — it's just scoped to the one window it's attached to. Views added to a window's Display show up only in that window.

tip

Every Window (including the main one) exposes its own Display through a display property.

Recap​

  • The Display is the root of your app's View hierarchy — but it isn't a View itself.
  • Add or remove top-level Views with display += view and display -= view.
  • Top-level Views have no parent, since the Display isn't a View.
  • A Display always fills whatever it's attached to — the page body, a hosting element, or a window — and your app doesn't need to know which.
  • Multi-window Desktop apps get one Display per window.

Next: you can add empty Views to the Display now — so let's find out what a View actually is, and how the whole hierarchy fits together, on Views.