Bogue tutorial — Hello world.

In this tutorial we will learn how to open a graphical window displaying a short text, like "Hello world". We then take advantage of this to familiarise with basic Bogue concepts.

Hello world

Let's start right ahead with the "minimal code" mentionned in Bogue's documentation:

open Bogue

let () =
  Widget.label "Hello world"
    |> Layout.resident
    |> Bogue.of_layout

We can copy this code in an OCaml toplevel and execute it; see here for general instructions.

A small window should pop up like this:

So, how does this work? Let's go through this again line by line.

First, instead of using the convenient |> operator, let's give names to the various steps; we have the following equivalent code:

let () =
  let widget = Widget.label "Hello world" in
  let layout = Layout.resident widget in
  let board = Bogue.of_layout layout in board

In Bogue, there are mainly two types of objects: widgets and layouts. Widgets are small graphics elements (buttons, images, etc.) and they can be combined into a Layout. Roughly speaking:

Here we create only one widget, of label type:

let widget = Widget.label "Hello world" in

and we install it in a layout, as in single resident:

let layout = Layout.resident widget in

Why is this function called "resident"? Well, if you browse Bogue's API, you will notice that Bogue uses a "housing" metaphor: a GUI is a big house with inhabitants (the widgets) living in various rooms (the layouts).

For this example, the layout we've just created is the only "room" in our house, so we use it to create our "board" (which is our complete GUI):

let board = Bogue.of_layout layout in

This board can be seen as our application, we run it using: board

Simple, isn't it?

More space

Well, of course there is more to it. For instance, you may find that the text label is a bit tight and needs more space around it. (In other words, the resident needs a larger room ;) )

So let's have a look at the documentation for the function Layout.resident:

val resident :
   ?name:string -> ?x:int -> ?y:int -> ?w:int -> ?h:int ->
   ?background:background ->
   ?draggable:bool ->
   ?canvas:Draw.canvas ->
   ?keyboard_focus:bool -> Widget.t -> t

We spot the optional parameters ?w and ?h which should set the desired widht and height of our layout. Let's try:

let () =
  Widget.label "Hello world"
    |> Layout.resident ~w:300 ~h:150
    |> Bogue.of_layout

Several widgets in a layout

Great, but the text feels alone... Suppose we want to display an image below our label.

Can we fit several residents in a room? Well, not really. Strictly speaking, a layout can contain only one widget. But, the trick is that, in Bogue, an element of type Layout.t can also contain a list of layouts; in this case we often call it a "house", since it contains a number of "rooms". Let's do this.

Side-note: In a layout, rooms can be arbitrarily nested. We have here the usual construction for a tree data structure: each node is either terminal (and called a leaf, which for us are widgets), or a vertex (for us, a layout), pointing to a list of sub-nodes.

The trunk of the tree (our main house, if you wish), will correspond to the layout associated with the window of the GUI. In Bogue we often call this special layout the "top layout", or "top house". (Yes, our tree grows top-down, like family trees.) See the "Layouts" tutorial for more details.

So, we want to display an image below the label. Our label is a widget:

let hello = Widget.label "Hello world"

An image is also a widget:

let image = Widget.image "bogue-icon.png"

Now, to put one on top of the other, we use the function Layout.tower_of_w (short for "tower of widget") which constructs a "tower":

let () =
  let hello = Widget.label "Hello world" in
  let image = Widget.image "bogue-icon.png" in
  let layout = Layout.tower_of_w [hello; image] in
  let board = Bogue.of_layout layout in board

This opens a window like this:

What exactly does this function Layout.tower_of_w do? It takes a list of widgets, and for each one, installs it in a room, as a resident. Then it constructs a new layout by piling up the rooms vertically.

The doc for tower_of_w shows interesting options. For instance, to center everything horizontally, use ~align:Draw.Center:

Exercise: vertical text

What about applying what we've just learned to write "Hello world" vertically?

Solution: Let's use Layout.tower_of_w to build a "tower" of letters.

let vertical text =
  Array.init (String.length text) (String.get text)
  |> Array.to_list
  |> (String.make 1)
  |> Widget.label
  |> Layout.tower_of_w

let () =
  vertical "Hello world"
  |> Bogue.of_layout

Et voila !

We now know enough Bogue to play with layouts full of text and image. But, of course, a crucial part of a GUI is missing: user interaction. This will be the goal of the "counter" tutorial.