API Documentation

Documentation for developing SailfishOS applications

Introduction to Sailfish Silica

The Sailfish Silica module provides the essential components for building the user interface of a Sailfish application.

It provides a set of UI components (based on the QtQuick component set) as well as utilities for styling your Sailfish applications and managing application behaviours. Using the Silica module to build your application UI not only makes it easier to implement through the Silica component set, but also ensures your app has the same look and feel as other Sailfish applications.

Creating Sailfish applications

Sailfish applications are simply QML-based applications: they are written through a combination of QML and C++, where the UI is built using QML components from the Silica and QtQuick modules (and any other third-party modules) and then launched from a C++ application by embedding the main QML file in a QQuickView.

The easiest way to create a Sailfish application is with the Sailfish SDK. The Sailfish SDK documentation shows how to create a Sailfish UI project to build a simple application. This project automatically creates a QML file with a simple UI definition and the C++ application to launch it.

The QML file is what defines the UI to be displayed by your application. Let's look at how you can expand this UI using various features of the Sailfish Silica module.

A simple Sailfish application UI

Here is a simple Sailfish application UI that shows a line of text in the center of the screen:

 import QtQuick 2.2
 import Sailfish.Silica 1.0

 ApplicationWindow {
     initialPage: Component {
         Page {
             Label {
                 text: "Hello world!"
                 anchors.centerIn: parent
             }
         }
     }
 }

There are three core aspects in creating this UI:

  • The QtQuick and Sailfish.Silica modules are imported in order to use their QML types
  • A Silica ApplicationWindow is declared as the top-level component
  • The displayed content is set by configuring the window to initially display a Silica Page with a centered Silica Label

ApplicationWindow is the top-level type of all Sailfish applications. This is the window that contains the application UI, and every window has a page stack that displays the current screen, or page, of the application. The ApplicationWindow initialPage property sets the first page displayed by the application, so in the above example, the app will initially show a single Page with a Label in the center. The initialPage can be set as a QML file URL, a Page instance, or a Component with a top-level Page (as in the above example).

Now that we have a simple application, we can expand the UI using some of the QML types from the Silica module.

Styling a simple Sailfish UI

Building on the previous example, here is an app that displays a slider, as well as a button that when clicked will add the slider's current value to a list:

 import QtQuick 2.2
 import Sailfish.Silica 1.0

 ApplicationWindow {
     initialPage: Component {
         Page {
             Column {
                 width: parent.width

                 // an interactive slider with a 0-100 range that steps by 1 when slided.
                 Slider {
                     id: slider
                     label: "A simple slider"
                     width: parent.width
                     minimumValue: 0; maximumValue: 100; stepSize: 1
                     valueText: value
                 }

                 Button {
                     text: "Save"
                     anchors.horizontalCenter: parent.horizontalCenter
                     onClicked: {
                         listModel.append({"sliderValue": "Value: " + slider.value})
                     }
                 }

                 Repeater {
                     model: ListModel { id: listModel }

                     Label { text: model.sliderValue }
                 }
             }
         }
     }
 }

As in the earlier example, the application and its initial page of content are created through the ApplicationWindow and Page QML types. Additionally, the app uses:

  • The Silica Slider type to show a horizontal slider
  • The Silica Button type to show a clickable button
  • The Silica Label type to show some text
  • The Column, ListModel and Repeater types from the standard QtQuick module for arranging the items within the layout and populating the list of slider values. Repeater creates an instance of its top-level child (in this case, a Label) for every entry in its model.

Although the UI functions as intended, it is missing some things that would give it the look and feel of a Sailfish application and also make it more presentable and usable. For example:

  • It would be ideal if the slider values could be removed from the list
  • The list entries are placed right next to the left edge of the page, instead of leaving some margin between the page edge and the list content
  • The page does not have any heading text
  • The page cannot be flicked downwards if the list of numbers exceeds the height of the page

These can be fixed by using some additional Silica types as well as the Theme object to style the UI appropriately.

Adding ListItem for list entries

The Silica ListItem type provides a standard UI component for list entries, with an optional ContextMenu. A context menu is a set of menu options that appear for an individual list item, allowing the user to trigger actions associated with it.

Here, the use of ListItem allows each list entry to be turned into an interactive item with a set of associated actions. This is done by making the repeater's top-level child a ListItem instead, and moving the Label into this child. Then, the ListItem menu property is set to a ContextMenu, with a MenuItem that triggers the removal of the item from the model:

 Repeater {
     model: ListModel { id: listModel }

     ListItem {
         width: parent.width

         Label {
             text: model.sliderValue
             anchors.verticalCenter: parent.verticalCenter
         }

         menu: ContextMenu {
             MenuItem {
                 text: "Remove"
                 onClicked: {
                     listModel.remove(model.index)
                 }
             }
         }
     }
 }

Now when the user presses and holds down on a list entry, the menu appears, allowing the removal of the item from the list.

Perhaps it would be better if the list entry was not removed immediately, in case the user should confirm its removal or be allowed to cancel the action. In Sailfish OS, this is applied through the remorse action concept, which allows the user to cancel a destructive action before its imminent execution. The RemorseItem and RemorsePopup types are used to present this option to the user: instead of performing the action immediately, the user is temporarily given the option of cancelling the action.

However, with a ListItem it's not necessary to use RemorseItem or RemorsePopup directly: as a convenience, the ListItem remorseAction() function internally shows a remorse action then call a specified function when the countdown finishes. So, modify the menu item's onClicked handler to call remorseAction() instead and pass in the function that calls ListModel remove() if the countdown reaches zero:

 Repeater {
     model: ListModel { id: listModel }

     ListItem {
         id: listEntry    // add an id so this can be referenced from onClicked() below
         width: parent.width

         Label {
             text: model.sliderValue
             anchors.verticalCenter: parent.verticalCenter
         }

         menu: ContextMenu {
             MenuItem {
                 text: "Remove"
                 onClicked: {
                     listEntry.remorseAction("Deleting", function() { listModel.remove(model.index) })
                 }
             }
         }
     }
 }

The label text still doesn't look quite right, though: the convention in Sailfish apps is that interactive items (that is, items that respond when pressed, like a Button or the list items in this example) color their text with a highlight-type color when pressed, or the default color otherwise. Also, there should be spacing between each list item and the left edge of the page, as well as spacing above and below the "Save" button.

Both of these issues can be fixed in a manner standard to Sailfish applications by styling with the Theme object.

Styling with the Theme object

The Theme object provides the common style characteristics of Sailfish UI components. You can use it to apply the standard Sailfish colors, fonts, sizes, margins and other look and feel characteristics to your UI.

For instance, Theme.horizontalPageMargin provides a standard size for the space between the edges of the page and the items within it, so this can be used to add a left-edge margin to the list entries in the example. Also, the Theme.primaryColor and Theme.highlightColor provide the default and highlight colors of the current Sailfish ambience, so bind the label's color property in each list entry to the appropriate color using the list item's highlighted property (which is true when the item is highlighted through a press action):

 ListItem {
     id: listEntry
     width: parent.width

     Label {
         color: listEntry.highlighted ? Theme.highlightColor : Theme.primaryColor    // color the text appropriately
         text: model.sliderValue
         x: Theme.horizontalPageMargin
         anchors.verticalCenter: parent.verticalCenter
     }

     menu: ContextMenu {
         MenuItem {
             text: "Remove"
             onClicked: {
                 listEntry.remorseAction("Deleting", function() { listModel.remove(model.index) })
             }
         }
     }
 }

To add space between the slider and the button, as well as between the button and the list, set the column's spacing to Theme.paddingLarge, a standard value for space between items on a page. Since this space should not be added between the items in the list of slider values, place the Repeater and its children within a new Column that uses the default zero-value spacing:

 Page {
     // main column
     Column {
         width: parent.width
         spacing: Theme.paddingLarge // add spacing between items in main column

         // Slider { ... }
         // Button { ... }

         // sub-column that contains the Repeater and its list items
         Column {
             width: parent.width

             // Repeater { ... }
         }
     }
 }

Further improvements

Generally, every page in a Silica application should display a header at the top, and it should be possible to scroll through the list of items if there are a lot of entries in the list.

The header can be added with a Silica PageHeader above the main column, and the list can be made scrollable by placing the Column within a SilicaFlickable. Additionally, you can add a VerticalScrollDecorator child to the flickable. This displays a vertical bar along the right edge of the flickable when it is flicked, to hint at the scroll position and total area of the flickable.

 Page {
     PageHeader {
         id: header
         title: "Sailfish example"
     }

     SilicaFlickable {
         anchors {
             top: header.bottom
             bottom: parent.bottom
             left: parent.left
             right: parent.right
         }

         // main column
         Column {
             // ... rest of main column contents
         }

         VerticalScrollDecorator {}
     }
 }

It would also be useful to allow the list to be cleared completely. This could be done with pulley menus: these are menus that are normally hidden but are slowly revealed when the parent flickable is dragged. In this case a PullDownMenu — a pulley menu that is shown at the top of a page — can be added to the SilicaFlickable:

 SilicaFlickable {
     PullDownMenu {
         MenuItem {
             text: "Clear"
             onClicked: listModel.clear()
         }
     }
     // ... rest of flickable code
 }

(It would be nice if the list was not cleared immediately but instead allowed the user to cancel the action within a few seconds, as was done using the remorseAction() function in the earlier example. In this case, it can be achieved with a RemorsePopup and calling execute() - try it out!)

Replacing the column list with a SilicaListView

Currently, the list data is displayed using a combination of SilicaFlickable, Column and Repeater. One thing to be aware of when using a Column is that all of its children are always created, even those that are currently off-screen. In this case, this is fine if the list is fairly small, but if there are hundreds or thousands of items, or if the delegates are complex with lots of details to be drawn, then the application performance is likely to suffer.

Instead, it would be preferable to use a SilicaListView, which is based on QtQuick's ListView type and only creates and renders the delegates that are visible (or are off-screen within the designated cacheBuffer). To do this, replace the SilicaFlickable with a SilicaListView, and move the model and delegate of the Repeater into the list view:

 Page {
     SilicaListView {
         anchors.fill: parent

         model: ListModel { id: listModel }

         delegate: ListItem {
             // ... rest of list item contents
         }

         // sub-column that contained the Repeater and its list items is no longer necessary
         // and can be removed

         VerticalScrollDecorator {}

         PullDownMenu {
             MenuItem {
                 text: "Clear"
                 onClicked: listModel.clear()
             }
         }
     }
 }

Additionally, the PageHeader and the main Column with the Slider and Button should be moved into the header of the list view, so that when it is scrolled, those components are scrolled together with the delegates of the list view. To do this, the items can be grouped together into a new layout using another Column:

 SilicaListView {
     header: Column {
         width: parent.width
         height: header.height + mainColumn.height + Theme.paddingLarge    // add space between button and first list item

         PageHeader {
             id: header
             title: "Sailfish example"
         }

         // main column
         Column {
             id: mainColumn
             // ... rest of main column contents
         }
     }

     // ... reset of SilicaListView code
 }

The finished result

In summary, to create a basic Sailfish UI with the Silica module:

Here is the completed example code:

 import QtQuick 2.2
 import Sailfish.Silica 1.0

 ApplicationWindow {
     initialPage: Component {
         Page {
             SilicaListView {
                 anchors.fill: parent

                 header: Column {
                     width: parent.width
                     height: header.height + mainColumn.height + Theme.paddingLarge

                     PageHeader {
                         id: header
                         title: "Sailfish example"
                     }

                     Column {
                         id: mainColumn
                         width: parent.width
                         spacing: Theme.paddingLarge

                         Slider {
                             id: slider
                             label: "A simple slider"
                             width: parent.width
                             minimumValue: 0; maximumValue: 100; stepSize: 1
                             valueText: value
                         }

                         Button {
                             text: "Save"
                             anchors.horizontalCenter: parent.horizontalCenter
                             onClicked: {
                                 listModel.append({"sliderValue": "Value: " + slider.value})
                             }
                         }
                     }
                 }

                 model: ListModel { id: listModel }

                 delegate: ListItem {
                     id: listEntry
                     width: parent.width

                     Label {
                         color: listEntry.highlighted ? Theme.highlightColor : Theme.primaryColor
                         text: model.sliderValue
                         x: Theme.horizontalPageMargin
                         anchors.verticalCenter: parent.verticalCenter
                     }

                     menu: ContextMenu {
                         MenuItem {
                             text: "Remove"
                             onClicked: {
                                 listEntry.remorseAction("Deleting", function() {
                                     listModel.remove(model.index)
                                 })
                             }
                         }
                     }
                 }

                 PullDownMenu {
                     MenuItem {
                         text: "Clear"
                         onClicked: listModel.clear()
                     }
                 }

                 VerticalScrollDecorator {}
             }
         }
     }
 }

Other useful Silica types

Aside from the Silica types previously mentioned, here are some other commonly used types:

See the Sailfish Silica Reference for the full list and reference documentation.

We use cookies to improve your user experience and to help us to develop our services. By continuing to browse the site, you approve of our use of cookies.