Build Hook

The build hook runs once just before any values are assigned to the WidgetState.

For Widgets

build hooks on widgets should be used when additional logic is necessary that sets multiple fields on WidgetState during widget instantiation. Note that such fields should not have assigned default values, as they will be overwritten when default values get applied after the build phase.

Example: A Widget may need to load data from elsewhere, via a file or HTTP request for one field, and a second field must be inferred from a value of the first field.

Here a simple example that uses the build hook to load a configuration file for the application:

example.json

{"name":"example"}
# main.nim
import std/json
import owlkettle
import owlkettle/bindings/gtk

type Config = object
  name: string

viewable App:
  config: Config

  hooks:
    build:
      state.config = "./src/example.json".readFile().parseJson().to(Config)

method view(state: AppState): Widget =
  result = gui:
    Window:
      Label:
        text = state.config.name

when not defined(owlkettleNimiDocs):
  brew(gui(App()))

build hooks on widgets also are inherited from the parent-widget. In those scenarios, during the build-phase owlkettle will first execute the build hook of the parent and then the build hook of the child.

To demonstrate this, here a small example:

import owlkettle

viewable Parent:
  hooks:
    build:
      echo "Parent.build"
    beforeBuild:
      echo "Parent.beforeBuild"
    afterBuild:
      echo "Parent.afterBuild"

method view(state: ParentState): Widget =
  gui:
    Label(text="Parent")

viewable Child of Parent:
  hooks:
    build:
      echo "Child.build"
    beforeBuild:
      echo "Child.beforeBuild"
    afterBuild:
      echo "Child.afterBuild"

method view(state: ChildState): Widget =
  gui:
    Label(text="Child")

viewable App2:
  discard

method view(app: App2State): Widget =
  result = gui:
    Window:
      Child()

when not defined(owlkettleNimiDocs):
  brew(gui(App2()))

This will print:

Child.beforeBuild
Parent.build
Child.build
Child.afterBuild

This makes sense, as only the purpose of the build hook (handling instantiating data not provided elsewhere) is also useful for any child-widgets.

Given that the purpose of beforeBuild is to handle instantiating renderables and afterBuild is about doing any extra handling of transforming of data given to the widget, both of which are very specific for their respective widget, it makes sense that they are "overwritten" instead of inherited.

For more info on the purpose of beforeBuild and afterBuild hooks, consult their respective sections in this file.

For Fields

A build hook is responsible for transfering the value of a field from the Widget to the WidgetState during the build-phase. Owlkettle provides default build hooks for every field, that simply assign the value of the field in the Widget to the field in the WidgetState if it is set. Overwriting the build hook is useful if you need to define custom behaviour, such as modifying the input slightly before initially assigning it to a field.

build hooks on fields should be used when additional logic is necessary that sets this single field on WidgetState.

Here an example for how a build hook on a field can be used:

import owlkettle

## The custom widget
viewable MyViewable:
  text: string

  hooks text:
    build:
      echo "Received via Widget:    ", widget.valText
      state.text = widget.valText & " build hook addition"
      echo "Applied to WidgetState: ", state.text

method view(state: MyViewableState): Widget =
  gui:
    Button(text = state.text):
      proc clicked() =
        echo "\nEvent triggering update"

## The App
viewable App3:
  discard

method view(app: App3State): Widget =
  result = gui:
    Window:
      MyViewable(text = "Example")

when not defined(owlkettleNimiDocs):
  brew(gui(App3()))

Note that this hook is not run during updates, so any changes here may be lost if an update overwrites them. Look at the update hook if you need that behaviour, or property hook if you need both.