Dark & Bright Themes for your app

Hey everyone!  I'm glad to finally be doing my first Cascades tutorial.  Maybe you've seen me on Twitter or on the BlackBerry Developer forums.  I'm the developer of PinGuin which was the winner of the 2013 BlackBerry Jam Camp Cambridge.  Over the past year I've been quickly getting to know the BlackBerry 10 OS and along with it, Cascades.  This is going to be the first of MANY tutorials to come.

This tutorial focuses on adding Dark and Bright themes your native Cascades app.  It's a very simple feature and one that I like to incorporate into my applications so that users can change my apps' themes to fit how they like their phones to look while using them!

The project is attached at the end of this tutorial for your pleasure and to use as a starting point if you'd like.  But first I would like to highlight some of the main features we use in this sample:

  • we use Qsettings to store what theme a user has chosen so that the app can reference it on startup
  • Qsettings are setup in the source folder
  • dropdown for selecting a theme

 

Screenshots

MockIt 2themes

 

 The following Settings.cpp and Settings.hpp are simply meant to setup your Qsettings.  You can just copy and past the entirety of the following code.

Settings.cpp

#include "Settings.hpp"

#include 

Settings::Settings() {
}

QString Settings::getValueFor(const QString &objectName, const QString &defaultValue) {
        QSettings settings;

        // If no value has been saved, return the default value.
        if (settings.value(objectName).isNull()) {
                return defaultValue;
        }

        // Otherwise, return the value stored in the settings object.
        return settings.value(objectName).toString();
}

void Settings::saveValueFor(const QString &objectName, const QString &inputValue) {
        // A new value is saved to the application settings object.
        QSettings settings;
        settings.setValue(objectName, QVariant(inputValue));
}

Settings::~Settings() {
}

Settings.hpp

#ifndef SETTINGS_HPP_
#define SETTINGS_HPP_

#include 

class Settings: public QObject {
        Q_OBJECT

public:
        Settings();
        virtual ~Settings();

        /**
         * This Invokable function gets a value from the QSettings,
         * if that value does not exist in the QSettings database, the default value is returned.
         *
         * @param objectName Index path to the item
         * @param defaultValue Used to create the data in the database when adding
         * @return If the objectName exists, the value of the QSettings object is returned.
         *         If the objectName doesn't exist, the default value is returned.
         */
        Q_INVOKABLE
        QString getValueFor(const QString &objectName, const QString &defaultValue);

        /**
         * This function sets a value in the QSettings database. This function should to be called
         * when a data value has been updated from QML
         *
         * @param objectName Index path to the item
         * @param inputValue new value to the QSettings database
         */
        Q_INVOKABLE
        void saveValueFor(const QString &objectName, const QString &inputValue);
};

#endif

Once you have your Qsettings setup it's time to move to your applicationui.cpp for referencing the Qsettings and to set the Settings in the QML for use later on.  The main things you need to add are:

 

#include <bb/cascades/Application>
#include "Settings.hpp"

 

AND

 

Settings *settings = new Settings();
                qml->setContextProperty("Settings", settings);

 

applicationui.cpp

#include "applicationui.hpp"
#include "Settings.hpp"

#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>
#include <bb/cascades/LocaleHandler>

using namespace bb::cascades;

ApplicationUI::ApplicationUI(bb::cascades::Application *app) :
        QObject(app)
{
    // prepare the localization
    m_pTranslator = new QTranslator(this);
    m_pLocaleHandler = new LocaleHandler(this);

    bool res = QObject::connect(m_pLocaleHandler, SIGNAL(systemLanguageChanged()), this, SLOT(onSystemLanguageChanged()));
    // This is only available in Debug builds
    Q_ASSERT(res);
    // Since the variable is not used in the app, this is added to avoid a
    // compiler warning
    Q_UNUSED(res);

    // initial load
    onSystemLanguageChanged();

    // Create scene document from main.qml asset, the parent is set
    // to ensure the document gets destroyed properly at shut down.
    QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);

    Settings *settings = new Settings();
                qml->setContextProperty("Settings", settings);

    // Create root object for the UI
    AbstractPane *root = qml->createRootObject();

    // Set created root object as the application scene
    app->setScene(root);
}

void ApplicationUI::onSystemLanguageChanged()
{
    QCoreApplication::instance()->removeTranslator(m_pTranslator);
    // Initiate, load and install the application translation files.
    QString locale_string = QLocale().name();
    QString file_name = QString("ThemeSample_%1").arg(locale_string);
    if (m_pTranslator->load(file_name, "app/native/qm")) {
        QCoreApplication::instance()->installTranslator(m_pTranslator);
    }
}

Next we move onto the main.cpp where we add a few things to communicate with the bar descriptor xml. They are the following:

#include <bb/cascades/Application>
#include "Settings.hpp"

 

+

 

using ::bb::cascades::Application;

 

+

 

QString getValue() {
Settings settings;
// use "theme" key for property showing what theme to use on application start
return settings.getValueFor("theme", "");
}

void myMessageOutput(QtMsgType type, const char* msg) {
Q_UNUSED(type);
   fprintf(stdout, "%s\n", msg);
   fflush(stdout);
}

 

+

 

qputenv("CASCADES_THEME", getValue().toUtf8());

 

AND

 

#ifndef QT_NO_DEBUG
   qInstallMsgHandler(myMessageOutput);
   #endif

main.cpp

#include <bb/cascades/Application>

#include 
#include 
#include "applicationui.hpp"
#include "Settings.hpp"

#include <Qt/qdeclarativedebug.h>

using ::bb::cascades::Application;
using namespace bb::cascades;

QString getValue() {
Settings settings;
// use "theme" key for property showing what theme to use on application start
return settings.getValueFor("theme", "");
}

void myMessageOutput(QtMsgType type, const char* msg) {
Q_UNUSED(type);
   fprintf(stdout, "%s\n", msg);
   fflush(stdout);
}

Q_DECL_EXPORT int main(int argc, char **argv)
{

	qputenv("CASCADES_THEME", getValue().toUtf8());

    Application app(argc, argv);

#ifndef QT_NO_DEBUG
   qInstallMsgHandler(myMessageOutput);
   #endif

    // Create the Application UI object, this is where the main.qml file
    // is loaded and the application scene is set.
    new ApplicationUI(&app);

    // Enter the application main event loop.
    return Application::exec();
}

You are now done having to do stuff in your src files! Now me move onto QML for the UI portion where we set the Qsetting based on what we select in the dropdown as well as have the current app theme be selected on creation completed.

main.qml

import bb.cascades 1.0

Page {
    Container {
        //Todo: fill me with QML
        DropDown {
            topPadding: 20
            id: themeDropDown
            title: qsTr("Theme Selection:") + Retranslate.onLocaleOrLanguageChanged
            
            Option {
                text: qsTr("Bright") + Retranslate.onLocaleOrLanguageChanged
                value: VisualStyle.Bright
            }
            
            Option {
                text: qsTr("Dark") + Retranslate.onLocaleOrLanguageChanged
                value: VisualStyle.Dark
            }
            onSelectedOptionChanged: {
                Settings.saveValueFor("theme", VisualStyle.Bright == themeDropDown.selectedValue ? "bright" : "dark");
            }
        }
        Label {
            // Localized text with the dynamic translation and locale updates support
            text: qsTr("Hello Themes") + Retranslate.onLocaleOrLanguageChanged
            textStyle.base: SystemDefaults.TextStyles.BigText
        }
        onCreationCompleted: {
            var theme = Settings.getValueFor("theme", VisualStyle.Bright == themeDropDown.selectedValue ? "bright" : "dark");
            themeDropDown.setSelectedIndex("bright" == theme ? 0 : 1);
        }
    }
}

 

So there you have it! You've successfully implemented an option for themes in your application. You can expand this to show or disable some features depending on what theme you have chosen as well as some other neat features.

Brandon Orr

Transportation Planner (University of Waterloo Graduate) & Blackberry 10 Developer (PinGuin App) part of the open source BB team.

Website: appworld.blackberry.com/webstore/vendor/65375/

Leave your comments

Post comment as a guest

0
  • No comments found
Subscribe to the official OSBB BBM Channel!

osbbchannelQR

C00013E89

Back to top