Supporting BlackBerry Passport – Fonts, Layouts and Logic

September 7, 2014 — Leave a comment

This is Part 3 of my ‘Support BlackBerry Passport’ series – you should read the other parts first:

  1. Supporting BlackBerry Passport – step one 10.2
  2. Supporting BlackBerry Passport – Keyboard Shortcuts

This article is the last one with some tips and tricks HowTo support the Passport from 10.2 SDK / API – part 4 will start using 10.3 SDK / API.

Most screenshots again are from my TimeTracker App – some are from one of my Conference2Go Apps: IoT Conference.

Let’s start with a ListView, where I displayed tracked time logs. Running on a Z30 the list looks different for Portrait and Landscape:

Z30-list-portrait

In Portrait the most important informations are visible from first row:

  • Time Category Image
  • Time tracked from HH:MM … to HH:MM
  • Duration HH:MM
  • Marker Image if tracked ‘over night’

second row gives some short information on Customer name (if there’s a customer connected) and Time Category Name.

Rotating to Landscape gives you more space to display some words from

  • Project (if available)
  • Order (if available)
  • Task (if available)

Z30-list-landscape

What to do on the Passport ? By default I’m using the Portrait Layout, but in this case there would be much empty space on the right side of the screen – so I decided to use the Landscape Layout as default layout for Passport. Here’s the result:

passport-list

Before Passport the layouting was easy done: the Container containing the Label fields for Landscape was only visible if device was rotated to landscape:

Container {
    id: column2Container
    visible: OrientationSupport.orientation == UIOrientation.Landscape
    layoutProperties: StackLayoutProperties {
        spaceQuota: 1.3
    }
    // Label .....
} // end column2Container

The Container becomes visble in Landscape – using spaceQuota arranging the available space for all the content displayed in StackLayout with orientation LeftToRight.

To get this also visible on Passport you could think I only had to change the visible property:

Container {
    ...
    visible: app.isPassport() || OrientationSupport.orientation == UIOrientation.Landscape
    ...
} // end column2Container

Unfortunately it’s not so easy because this Container is part of an ListItem and from inside a ListItemComponent I don’t have access to C++ methods, so app.isPassport() always will return false. To make it run we have to delegate this to the ListView where the ListItemComponent lives in:

ListView {
    id: trackedList
    // ...
    property bool column2Visible: app.isPassport() || OrientationSupport.orientation == UIOrientation.Landscape
    // ...
    listItemComponents: [
        ListItemComponent {
            type: "item"
            CustomListItem {
                id: itemRoot
                dividerVisible: true
                highlightAppearance: HighlightAppearance.Full
                TrackedListItemContainer {
                    // extra QML file layouting our ListItem
                } // end outerItemContainer
            } // end itemRoot
        }, // end ListItemComponent item

    ] // end listItemComponents
    // headerItem
    // onTriggered{} and other functions
} // end ListView

Now we can access the property from inside the ListItemComponent (TrackedListitemContainer):

Container {
    // ...
    visible: itemRoot.ListItem.view.column2Visible
    // ...
} // end column2Container

At the end it’s easy as soon as you understood how ListItemComponents are working.

UI on Passport’s High-Density (453 dpi) screen looks great🙂

Even small Fonts are looking fine. Tap on the screenshot above  and zoom in to verify.

Please be aware that this high density can be a problem for users – not all eyes are able to read such small fonts and not everyone uses high end glasses to make it readable.

In my apps I’m following the UI Guidelines and I’m using SystemFontStyles like TitleText, SubTitleText, … and it’s up to the OS to choose the font and size.

On Z30 it’s very rare to hear users complaining about too small fonts, but on high-density devices like Passport this can happen. Fortunately users can change this from System Settings | Display:

Settings_Display8vs14

Changing this Font Size immediately affects all apps – even already running apps.

In many cases you have nothing to do if your design follows the rules on adaptive layouting. Here are screenshots from my IoT Conference App:

Sessions using default font size of 8:

iotconf_list_8

and here the same list with font size of 14:

iotconf_list_14

Hint: this conference app must be slightly  tuned for Passport and I haven’t done this yet: Icons are too small – but text is working for all sizes.

Back to my TimeTracker App where the List Layout is very specific and won’t survive larger fonts:

passport_14_problem

Now the Duration is missing because there’s not enough space anymore using such a large font. Changing to ’12’ duration will be partly there – changing to larger then ’14’ from…to time will only be visible partly. Both informations are important and should always be visible independent from Font Size.

Here’s a solution HowTo survive such kind of layouts:

Label {
    text: ListItemData.hourMinuteLocal + "-" + ListItemData.hourMinuteLocalStop
    textFit.maxFontSizeValue: itemRoot.ListItem.view.titleTextMaxSize
    textStyle.base: SystemDefaults.TextStyles.TitleText
    textStyle.fontWeight: FontWeight.W500
}

There are some ‘textFit’ properties on Labels where you can set min and max font sizes and more. In this case I want to limit the font size for TitleText. The property itself is defined at ListView:

ListView {
    id: trackedList
    // ...
    property bool column2Visible: app.isPassport() || OrientationSupport.orientation == UIOrientation.Landscape
    property int categoryImageSize: app.isPassport()?120:80
    property int imageSize: app.isPassport()?120:81
    property int markerImageSize: app.isPassport()?92:61
    property int titleTextMaxSize: app.isPassport()?11:10
    property int bodyTextMaxSize: app.isPassport()?10:9
    // ...
} // end ListView

As you can see I have different max values for Passport and other devices.

Some rules:

  • At first always design your layout using default font size to get an optimal result
  • Then change font sizes from Settings
  • Try to find a layout design working with all sizes
    • If not find out maximum values

Don’t forget: all of this is HowTo make it work from 10.2 without switching to 10.3 API. Using 10.3 and ‘DU’s’ (Design Units) and asset folders for density / size / … there are more options to do it then from 10.2 where you only can use the 1440×1440 static assets folder to place assets used by passport.

As soon as you’re using multiple QML files with same name  for different devices, density etc. development becomes more difficult. At first you should always try to do your work from one single QML file for a Page, Sheet, Container. Later when your UI is more stable, duplicate the files and do the device-specific layouting. Doing this too early means all changes must me done in multiple files.

Here’s a tip HowTo use functions with some UI Logic from more then one QML File without duplicating the functions itself:

  • Create an extra QML File for a Container
    • This Container only contains functions, no UI Controls
  • Attach this Container to all Pages (per ex. PageXYZ.qml in assets/ and also assets/1440×1440) calling these functions

Then you only have to manage the function from one place.

Here’s the Container containing UI Logic. In this case there are two functions to check if a tracked data item is overlapping start or stop time with already tracked items. Functions are filling a temp GroupDataModel to check this, so it’s also inside the Container – no one else needs it.

I’m placing such Containers inside an extra folder: assets/ui_logic to make it clear that these qml files only contain some logic.

This is assets/ui_logic/CheckOverlappingTimes.qml:

import bb.cascades 1.2

Container {
    id: checkOverlappingTimes
    
    attachedObjects: [
        GroupDataModel {
            // f,t, id
            id: dataModelOverlap
            sortingKeys: [ "f" ]
            sortedAscending: false
            grouping: ItemGrouping.ByFullValue
        }
    ]
    
    function isStartNotOverlapping(start){      
        // your logic
    }
    
    function isStartStopNotOverlapping(uuid, start, stop){
        // your logic
    }
}

Pages calling these functions:

import "ui_logic"
Page {
    id: myPage
    // ...
    attachedObjects: [
        // dummi
        CheckOverlappingTimes {
            id: checkOverlappingTimes
            visible: false
        }
    ]
    // ...
    actions: [
       ActionItem {
            id: saveAction
            title: qsTr("Save")
            ActionBar.placement: ActionBarPlacement.OnBar
            imageSource: "asset:///images/save.png"
            onTriggered: {
                var startStopValid = checkOverlappingTimes.isStartStopNotOverlapping(uuid, startTime, stopTime)
                if(!startStopValid){
                    // do something
                    return
                }
            }
        },    
    ]
    // ...
}

Now all calls to these functions are delegated to our special Container.

Hope you got some ideas where to look in your apps to support Passport Device from your 10.2 API.

Next article will go one step further and use 10.3 API which means you have to support two different APIs – I’ll tell you how I’m doing this.

Have Fun with BlackBerry Passport !

… and don’t forget: See the bigger picture soon in Toronto, London or Dubai.

No Comments

Be the first to start the conversation!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s