Part 2: Using a Leaflet

Overview

When run, the application described in this part of the tutorial shows a window containing text labels that can be shown side-by-side or individually depending on the available screen space.

A screenshot of the application running in the phone environment

The main source code for the application can be found in the main.py file within the src directory. The purpose of the other files is explained in other tutorials, such as Your First Application.

The Program

The main program is longer than in the previous part of this tutorial, so we will not show it all here. We will focus on the parts that are specific to this example.

Relevant Modules

The program begins by importing the modules it needs to create a user interface. In addition to the standard Gtk module we use the Handy module in order to access the widgets of the libhandy library:

gi.require_version('Handy', '0.0')
from gi.repository import Handy
Handy.init()

When importing the Handy module, it is important to specify the version of the API that will be used.

Creating a Title Bar

The Application class provides the usual methods to set up the application and perform tasks when it is run. In the do_activate method we set up the user interface, beginning by creating a title bar:

    def do_activate(self):
        window = Gtk.ApplicationWindow(application=self)
        window.set_icon_name('com.example.leaflet_example')

        title_bar = Handy.TitleBar()
        header = Gtk.HeaderBar(title='Leaflet', show_close_button=True)

        title_bar.add(header)
        window.set_titlebar(title_bar)

This is done in the same way as in the previous part of this tutorial.

Using a Leaflet

The contents of the window itself are managed by a Leaflet from the libhandy library which is created with two default attributes that describe how transitions are performed – these enable animations for layout changes inside the window:

        self.content_leaflet = Handy.Leaflet(
            child_transition_type='slide',
            mode_transition_type='slide'
        )

The Leaflet widget is a container that manages the dimensions of its children. We divide these into two pages that can be displayed side-by-side if there is enough space, or are otherwise shown individually and can be flipped between when the leaflet is folded.

Composing the Pages

We define the left page first, using a Gtk.Box that contains a label and a button:

        page1 = Gtk.Box(orientation='vertical')

        label = Gtk.Label(wrap=True)
        label.set_markup(
            '<span size="xx-large" weight="bold">Leaflet</span>\n\n'
            '<span size="large">This example shows how to use a leaflet.\n\n'
            'Leaflets make the user interface adaptive.\n\n'
            'Resize the window to see more content.\n\n'
            'Otherwise click on the button below.</span>'
        )
        label.set_padding(8, 0)

        more_button = Gtk.Button(
            label='Read more…',
            halign='center'
        )

        page1.pack_start(label, True, True, 16)
        page1.pack_start(more_button, False, True, 16)

The right page is defined in a similar way, with a label and button inside a container:

        page2 = Gtk.Box(orientation='vertical')

        label = Gtk.Label(wrap=True)
        label.set_markup(
            '<span size="xx-large" weight="bold"> </span>\n\n'
            '<span size="large">\nIf the window is too small, this text '
            'will not be visible.\n\n'
            'You need to click the button to show it.\n\n'
            'If the window is large enough, all the text '
            'can be shown.</span>'
        )
        label.set_padding(8, 0)

        back_button = Gtk.Button(
            label='Go back…',
            halign='center'
        )

        page2.pack_start(label, True, True, 16)
        page2.pack_start(back_button, False, True, 16)

The buttons in these pages will be connected later in the method.

Adding Pages to the Leaflet

With the two pages defined, we add them to the leaflet using the standard add method:

        self.content_leaflet.add(page1)
        self.content_leaflet.add(page2)

        window.add(self.content_leaflet)

We also add the leaflet to the window to ensure that it is shown.

Binding Properties and Connecting Signals

Since the pages can be shown side-by-side, we want to hide the buttons in that situation so that they are not confusing for users.

We bind each button’s visible property to the folded state of the content leaflet. This ensures that the button will only appear when the leaflet is folded:

        self.content_leaflet.bind_property(
            'folded',
            more_button, 'visible',
            GObject.BindingFlags.SYNC_CREATE)

        self.content_leaflet.bind_property(
            'folded',
            back_button, 'visible',
            GObject.BindingFlags.SYNC_CREATE)

        more_button.connect('clicked', self.show_page, page2)
        back_button.connect('clicked', self.show_page, page1)

We also connect the clicked signal for each button to a method that will show the page associated with that button. The widget is supplied as the last argument to each connection.

Setting a Default Size

Finally, we show the window and its contents:

        window.set_default_size(320, 512)
        window.show_all()

Note that we define a default size for the window. This helps to inform the leaflet about its initial state when the window is shown – whether it should be folded or expanded. Without this information the buttons may appear when the leaflet is expanded.

Handling Button Clicks

The last thing to define is the show_page method that responds to the clicked signals from the buttons:

    def show_page(self, button, page):
        self.content_leaflet.set_visible_child(page)

Here, the page supplied as an argument is passed to the leaflet so that it can be shown. The leaflet performs the transition defined earlier in order to display it. Since a slide transition was specified, the current page slides smoothly away and the new page slides into its place.

Running the Application

See the Building the Applications and Packaging the Applications sections for information about building, packaging and running the application.

Summary

This part of the tutorial showed the use of the Handy.Leaflet widget to provide an adaptive container for the contents of a window, showing as much as possible if there is enough space, and otherwise presenting its contents as a foldable stack of pages.

Each page is defined as a Gtk.Box then added to the leaflet using the leaflet’s add method. Connections from buttons in the pages to a method allow the current page to be changed on a button click, using the leaflet’s set_visible_child method.

By binding the folded property of the leaflet to the visible properties of the two buttons, we ensure that they are only visible when the leaflet is folded.

In the next part of this tutorial we will use two leaflets to synchronize the window’s title bar and its visible contents.